mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-25 09:02:00 +00:00
Add WITH IMPLICIT
, fix error with implicit grants
This commit is contained in:
parent
229ead5f91
commit
2a107e3429
@ -351,7 +351,7 @@ Shows privileges for a user.
|
||||
**Syntax**
|
||||
|
||||
``` sql
|
||||
SHOW GRANTS [FOR user1 [, user2 ...]]
|
||||
SHOW GRANTS [FOR user1 [, user2 ...]] [WITH IMPLICIT]
|
||||
```
|
||||
|
||||
If user is not specified, the query returns privileges for the current user.
|
||||
|
@ -234,7 +234,7 @@ SHOW DICTIONARIES FROM db LIKE '%reg%' LIMIT 2
|
||||
### Синтаксис {#show-grants-syntax}
|
||||
|
||||
``` sql
|
||||
SHOW GRANTS [FOR user]
|
||||
SHOW GRANTS [FOR user1 [, user2 ...]] [WITH IMPLICIT]
|
||||
```
|
||||
|
||||
Если пользователь не задан, запрос возвращает привилегии текущего пользователя.
|
||||
|
@ -110,7 +110,7 @@ SHOW DICTIONARIES FROM db LIKE '%reg%' LIMIT 2
|
||||
### 语法 {#show-grants-syntax}
|
||||
|
||||
``` sql
|
||||
SHOW GRANTS [FOR user]
|
||||
SHOW GRANTS [FOR user1 [, user2 ...]] [WITH IMPLICIT]
|
||||
```
|
||||
|
||||
如果未指定用户,输出当前用户的权限
|
||||
|
@ -1155,9 +1155,6 @@ private:
|
||||
|
||||
calculateMinMaxFlags();
|
||||
|
||||
if (!isLeaf())
|
||||
return;
|
||||
|
||||
auto new_flags = function(flags, min_flags_with_children, max_flags_with_children, level, grant_option);
|
||||
|
||||
if (new_flags != flags)
|
||||
|
@ -64,196 +64,6 @@ namespace
|
||||
}
|
||||
|
||||
|
||||
AccessRights addImplicitAccessRights(const AccessRights & access, const AccessControl & access_control)
|
||||
{
|
||||
AccessFlags max_flags;
|
||||
|
||||
auto modifier = [&](const AccessFlags & flags,
|
||||
const AccessFlags & min_flags_with_children,
|
||||
const AccessFlags & max_flags_with_children,
|
||||
const size_t level,
|
||||
bool /* grant_option */) -> AccessFlags
|
||||
{
|
||||
AccessFlags res = flags;
|
||||
|
||||
/// CREATE_TABLE => CREATE_VIEW, DROP_TABLE => DROP_VIEW, ALTER_TABLE => ALTER_VIEW
|
||||
static const AccessFlags create_table = AccessType::CREATE_TABLE;
|
||||
static const AccessFlags create_view = AccessType::CREATE_VIEW;
|
||||
static const AccessFlags drop_table = AccessType::DROP_TABLE;
|
||||
static const AccessFlags drop_view = AccessType::DROP_VIEW;
|
||||
static const AccessFlags alter_table = AccessType::ALTER_TABLE;
|
||||
static const AccessFlags alter_view = AccessType::ALTER_VIEW;
|
||||
|
||||
if (res & create_table)
|
||||
res |= create_view;
|
||||
|
||||
if (res & drop_table)
|
||||
res |= drop_view;
|
||||
|
||||
if (res & alter_table)
|
||||
res |= alter_view;
|
||||
|
||||
/// CREATE TABLE (on any database/table) => CREATE_TEMPORARY_TABLE (global)
|
||||
static const AccessFlags create_temporary_table = AccessType::CREATE_TEMPORARY_TABLE;
|
||||
if ((level == 0) && (max_flags_with_children & create_table))
|
||||
res |= create_temporary_table;
|
||||
|
||||
/// CREATE TABLE (on any database/table) => CREATE_ARBITRARY_TEMPORARY_TABLE (global)
|
||||
static const AccessFlags create_arbitrary_temporary_table = AccessType::CREATE_ARBITRARY_TEMPORARY_TABLE;
|
||||
if ((level == 0) && (max_flags_with_children & create_table))
|
||||
res |= create_arbitrary_temporary_table;
|
||||
|
||||
/// ALTER_TTL => ALTER_MATERIALIZE_TTL
|
||||
static const AccessFlags alter_ttl = AccessType::ALTER_TTL;
|
||||
static const AccessFlags alter_materialize_ttl = AccessType::ALTER_MATERIALIZE_TTL;
|
||||
if (res & alter_ttl)
|
||||
res |= alter_materialize_ttl;
|
||||
|
||||
/// RELOAD_DICTIONARY (global) => RELOAD_EMBEDDED_DICTIONARIES (global)
|
||||
static const AccessFlags reload_dictionary = AccessType::SYSTEM_RELOAD_DICTIONARY;
|
||||
static const AccessFlags reload_embedded_dictionaries = AccessType::SYSTEM_RELOAD_EMBEDDED_DICTIONARIES;
|
||||
if ((level == 0) && (min_flags_with_children & reload_dictionary))
|
||||
res |= reload_embedded_dictionaries;
|
||||
|
||||
/// any column flag => SHOW_COLUMNS => SHOW_TABLES => SHOW_DATABASES
|
||||
/// any table flag => SHOW_TABLES => SHOW_DATABASES
|
||||
/// any dictionary flag => SHOW_DICTIONARIES => SHOW_DATABASES
|
||||
/// any database flag => SHOW_DATABASES
|
||||
static const AccessFlags show_columns = AccessType::SHOW_COLUMNS;
|
||||
static const AccessFlags show_tables = AccessType::SHOW_TABLES;
|
||||
static const AccessFlags show_dictionaries = AccessType::SHOW_DICTIONARIES;
|
||||
static const AccessFlags show_tables_or_dictionaries = show_tables | show_dictionaries;
|
||||
static const AccessFlags show_databases = AccessType::SHOW_DATABASES;
|
||||
|
||||
if (res & AccessFlags::allColumnFlags())
|
||||
res |= show_columns;
|
||||
|
||||
if ((res & AccessFlags::allTableFlags())
|
||||
|| (level <= 2 && (res & show_columns))
|
||||
|| (level == 2 && (max_flags_with_children & show_columns)))
|
||||
{
|
||||
res |= show_tables;
|
||||
}
|
||||
|
||||
if (res & AccessFlags::allDictionaryFlags())
|
||||
res |= show_dictionaries;
|
||||
|
||||
if ((res & AccessFlags::allDatabaseFlags())
|
||||
|| (level <= 1 && (res & show_tables_or_dictionaries))
|
||||
|| (level == 1 && (max_flags_with_children & show_tables_or_dictionaries)))
|
||||
{
|
||||
res |= show_databases;
|
||||
}
|
||||
|
||||
max_flags |= res;
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
AccessRights res = access;
|
||||
res.modifyFlags(modifier);
|
||||
|
||||
/// If "select_from_system_db_requires_grant" is enabled we provide implicit grants only for a few tables in the system database.
|
||||
if (access_control.doesSelectFromSystemDatabaseRequireGrant())
|
||||
{
|
||||
const char * always_accessible_tables[] = {
|
||||
/// Constant tables
|
||||
"one",
|
||||
|
||||
/// "numbers", "numbers_mt", "zeros", "zeros_mt" were excluded because they can generate lots of values and
|
||||
/// that can decrease performance in some cases.
|
||||
|
||||
"contributors",
|
||||
"licenses",
|
||||
"time_zones",
|
||||
"collations",
|
||||
|
||||
"formats",
|
||||
"privileges",
|
||||
"data_type_families",
|
||||
"database_engines",
|
||||
"table_engines",
|
||||
"table_functions",
|
||||
"aggregate_function_combinators",
|
||||
|
||||
"functions", /// Can contain user-defined functions
|
||||
|
||||
/// The following tables hide some rows if the current user doesn't have corresponding SHOW privileges.
|
||||
"databases",
|
||||
"tables",
|
||||
"columns",
|
||||
|
||||
/// Specific to the current session
|
||||
"settings",
|
||||
"current_roles",
|
||||
"enabled_roles",
|
||||
"quota_usage"
|
||||
};
|
||||
|
||||
for (const auto * table_name : always_accessible_tables)
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE, table_name);
|
||||
|
||||
if (max_flags.contains(AccessType::SHOW_USERS))
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE, "users");
|
||||
|
||||
if (max_flags.contains(AccessType::SHOW_ROLES))
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE, "roles");
|
||||
|
||||
if (max_flags.contains(AccessType::SHOW_ROW_POLICIES))
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE, "row_policies");
|
||||
|
||||
if (max_flags.contains(AccessType::SHOW_SETTINGS_PROFILES))
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE, "settings_profiles");
|
||||
|
||||
if (max_flags.contains(AccessType::SHOW_QUOTAS))
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE, "quotas");
|
||||
}
|
||||
else
|
||||
{
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE);
|
||||
}
|
||||
|
||||
/// If "select_from_information_schema_requires_grant" is enabled we don't provide implicit grants for the information_schema database.
|
||||
if (!access_control.doesSelectFromInformationSchemaRequireGrant())
|
||||
{
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::INFORMATION_SCHEMA);
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::INFORMATION_SCHEMA_UPPERCASE);
|
||||
}
|
||||
|
||||
/// There is overlap between AccessType sources and table engines, so the following code avoids user granting twice.
|
||||
|
||||
/// Sync SOURCE and TABLE_ENGINE, so only need to check TABLE_ENGINE later.
|
||||
if (access_control.doesTableEnginesRequireGrant())
|
||||
{
|
||||
for (const auto & source_and_table_engine : source_and_table_engines)
|
||||
{
|
||||
const auto & source = std::get<0>(source_and_table_engine);
|
||||
if (res.isGranted(source))
|
||||
{
|
||||
const auto & table_engine = std::get<1>(source_and_table_engine);
|
||||
res.grant(AccessType::TABLE_ENGINE, table_engine);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Add TABLE_ENGINE on * and then remove TABLE_ENGINE on particular engines.
|
||||
res.grant(AccessType::TABLE_ENGINE);
|
||||
for (const auto & source_and_table_engine : source_and_table_engines)
|
||||
{
|
||||
const auto & source = std::get<0>(source_and_table_engine);
|
||||
if (!res.isGranted(source))
|
||||
{
|
||||
const auto & table_engine = std::get<1>(source_and_table_engine);
|
||||
res.revoke(AccessType::TABLE_ENGINE, table_engine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
std::array<UUID, 1> to_array(const UUID & id)
|
||||
{
|
||||
std::array<UUID, 1> ids;
|
||||
@ -274,6 +84,196 @@ namespace
|
||||
}
|
||||
|
||||
|
||||
AccessRights ContextAccess::addImplicitAccessRights(const AccessRights & access, const AccessControl & access_control)
|
||||
{
|
||||
AccessFlags max_flags;
|
||||
|
||||
auto modifier = [&](const AccessFlags & flags,
|
||||
const AccessFlags & min_flags_with_children,
|
||||
const AccessFlags & max_flags_with_children,
|
||||
const size_t level,
|
||||
bool /* grant_option */) -> AccessFlags
|
||||
{
|
||||
AccessFlags res = flags;
|
||||
|
||||
/// CREATE_TABLE => CREATE_VIEW, DROP_TABLE => DROP_VIEW, ALTER_TABLE => ALTER_VIEW
|
||||
static const AccessFlags create_table = AccessType::CREATE_TABLE;
|
||||
static const AccessFlags create_view = AccessType::CREATE_VIEW;
|
||||
static const AccessFlags drop_table = AccessType::DROP_TABLE;
|
||||
static const AccessFlags drop_view = AccessType::DROP_VIEW;
|
||||
static const AccessFlags alter_table = AccessType::ALTER_TABLE;
|
||||
static const AccessFlags alter_view = AccessType::ALTER_VIEW;
|
||||
|
||||
if (res & create_table)
|
||||
res |= create_view;
|
||||
|
||||
if (res & drop_table)
|
||||
res |= drop_view;
|
||||
|
||||
if (res & alter_table)
|
||||
res |= alter_view;
|
||||
|
||||
/// CREATE TABLE (on any database/table) => CREATE_TEMPORARY_TABLE (global)
|
||||
static const AccessFlags create_temporary_table = AccessType::CREATE_TEMPORARY_TABLE;
|
||||
if ((level == 0) && (max_flags_with_children & create_table))
|
||||
res |= create_temporary_table;
|
||||
|
||||
/// CREATE TABLE (on any database/table) => CREATE_ARBITRARY_TEMPORARY_TABLE (global)
|
||||
static const AccessFlags create_arbitrary_temporary_table = AccessType::CREATE_ARBITRARY_TEMPORARY_TABLE;
|
||||
if ((level == 0) && (max_flags_with_children & create_table))
|
||||
res |= create_arbitrary_temporary_table;
|
||||
|
||||
/// ALTER_TTL => ALTER_MATERIALIZE_TTL
|
||||
static const AccessFlags alter_ttl = AccessType::ALTER_TTL;
|
||||
static const AccessFlags alter_materialize_ttl = AccessType::ALTER_MATERIALIZE_TTL;
|
||||
if (res & alter_ttl)
|
||||
res |= alter_materialize_ttl;
|
||||
|
||||
/// RELOAD_DICTIONARY (global) => RELOAD_EMBEDDED_DICTIONARIES (global)
|
||||
static const AccessFlags reload_dictionary = AccessType::SYSTEM_RELOAD_DICTIONARY;
|
||||
static const AccessFlags reload_embedded_dictionaries = AccessType::SYSTEM_RELOAD_EMBEDDED_DICTIONARIES;
|
||||
if ((level == 0) && (min_flags_with_children & reload_dictionary))
|
||||
res |= reload_embedded_dictionaries;
|
||||
|
||||
/// any column flag => SHOW_COLUMNS => SHOW_TABLES => SHOW_DATABASES
|
||||
/// any table flag => SHOW_TABLES => SHOW_DATABASES
|
||||
/// any dictionary flag => SHOW_DICTIONARIES => SHOW_DATABASES
|
||||
/// any database flag => SHOW_DATABASES
|
||||
static const AccessFlags show_columns = AccessType::SHOW_COLUMNS;
|
||||
static const AccessFlags show_tables = AccessType::SHOW_TABLES;
|
||||
static const AccessFlags show_dictionaries = AccessType::SHOW_DICTIONARIES;
|
||||
static const AccessFlags show_tables_or_dictionaries = show_tables | show_dictionaries;
|
||||
static const AccessFlags show_databases = AccessType::SHOW_DATABASES;
|
||||
|
||||
if (res & AccessFlags::allColumnFlags())
|
||||
res |= show_columns;
|
||||
|
||||
if ((res & AccessFlags::allTableFlags())
|
||||
|| (level <= 2 && (res & show_columns))
|
||||
|| (level == 2 && (max_flags_with_children & show_columns)))
|
||||
{
|
||||
res |= show_tables;
|
||||
}
|
||||
|
||||
if (res & AccessFlags::allDictionaryFlags())
|
||||
res |= show_dictionaries;
|
||||
|
||||
if ((res & AccessFlags::allDatabaseFlags())
|
||||
|| (level <= 1 && (res & show_tables_or_dictionaries))
|
||||
|| (level == 1 && (max_flags_with_children & show_tables_or_dictionaries)))
|
||||
{
|
||||
res |= show_databases;
|
||||
}
|
||||
|
||||
max_flags |= res;
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
AccessRights res = access;
|
||||
res.modifyFlags(modifier);
|
||||
|
||||
/// If "select_from_system_db_requires_grant" is enabled we provide implicit grants only for a few tables in the system database.
|
||||
if (access_control.doesSelectFromSystemDatabaseRequireGrant())
|
||||
{
|
||||
const char * always_accessible_tables[] = {
|
||||
/// Constant tables
|
||||
"one",
|
||||
|
||||
/// "numbers", "numbers_mt", "zeros", "zeros_mt" were excluded because they can generate lots of values and
|
||||
/// that can decrease performance in some cases.
|
||||
|
||||
"contributors",
|
||||
"licenses",
|
||||
"time_zones",
|
||||
"collations",
|
||||
|
||||
"formats",
|
||||
"privileges",
|
||||
"data_type_families",
|
||||
"database_engines",
|
||||
"table_engines",
|
||||
"table_functions",
|
||||
"aggregate_function_combinators",
|
||||
|
||||
"functions", /// Can contain user-defined functions
|
||||
|
||||
/// The following tables hide some rows if the current user doesn't have corresponding SHOW privileges.
|
||||
"databases",
|
||||
"tables",
|
||||
"columns",
|
||||
|
||||
/// Specific to the current session
|
||||
"settings",
|
||||
"current_roles",
|
||||
"enabled_roles",
|
||||
"quota_usage"
|
||||
};
|
||||
|
||||
for (const auto * table_name : always_accessible_tables)
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE, table_name);
|
||||
|
||||
if (max_flags.contains(AccessType::SHOW_USERS))
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE, "users");
|
||||
|
||||
if (max_flags.contains(AccessType::SHOW_ROLES))
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE, "roles");
|
||||
|
||||
if (max_flags.contains(AccessType::SHOW_ROW_POLICIES))
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE, "row_policies");
|
||||
|
||||
if (max_flags.contains(AccessType::SHOW_SETTINGS_PROFILES))
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE, "settings_profiles");
|
||||
|
||||
if (max_flags.contains(AccessType::SHOW_QUOTAS))
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE, "quotas");
|
||||
}
|
||||
else
|
||||
{
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::SYSTEM_DATABASE);
|
||||
}
|
||||
|
||||
/// If "select_from_information_schema_requires_grant" is enabled we don't provide implicit grants for the information_schema database.
|
||||
if (!access_control.doesSelectFromInformationSchemaRequireGrant())
|
||||
{
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::INFORMATION_SCHEMA);
|
||||
res.grant(AccessType::SELECT, DatabaseCatalog::INFORMATION_SCHEMA_UPPERCASE);
|
||||
}
|
||||
|
||||
/// There is overlap between AccessType sources and table engines, so the following code avoids user granting twice.
|
||||
|
||||
/// Sync SOURCE and TABLE_ENGINE, so only need to check TABLE_ENGINE later.
|
||||
if (access_control.doesTableEnginesRequireGrant())
|
||||
{
|
||||
for (const auto & source_and_table_engine : source_and_table_engines)
|
||||
{
|
||||
const auto & source = std::get<0>(source_and_table_engine);
|
||||
if (res.isGranted(source))
|
||||
{
|
||||
const auto & table_engine = std::get<1>(source_and_table_engine);
|
||||
res.grant(AccessType::TABLE_ENGINE, table_engine);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Add TABLE_ENGINE on * and then remove TABLE_ENGINE on particular engines.
|
||||
res.grant(AccessType::TABLE_ENGINE);
|
||||
for (const auto & source_and_table_engine : source_and_table_engines)
|
||||
{
|
||||
const auto & source = std::get<0>(source_and_table_engine);
|
||||
if (!res.isGranted(source))
|
||||
{
|
||||
const auto & table_engine = std::get<1>(source_and_table_engine);
|
||||
res.revoke(AccessType::TABLE_ENGINE, table_engine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<const ContextAccess> ContextAccess::fromContext(const ContextPtr & context)
|
||||
{
|
||||
return ContextAccessWrapper::fromContext(context)->getAccess();
|
||||
|
@ -133,6 +133,8 @@ public:
|
||||
/// Checks if grantees are allowed for the current user, throws an exception if not.
|
||||
void checkGranteesAreAllowed(const std::vector<UUID> & grantee_ids) const;
|
||||
|
||||
static AccessRights addImplicitAccessRights(const AccessRights & access, const AccessControl & access_control);
|
||||
|
||||
ContextAccess(const AccessControl & access_control_, const Params & params_);
|
||||
~ContextAccess();
|
||||
|
||||
|
@ -32,7 +32,8 @@ namespace
|
||||
ASTs getGrantQueriesImpl(
|
||||
const T & grantee,
|
||||
const AccessControl * access_control /* not used if attach_mode == true */,
|
||||
bool attach_mode = false)
|
||||
bool attach_mode = false,
|
||||
bool with_implicit = false)
|
||||
{
|
||||
ASTs res;
|
||||
|
||||
@ -41,7 +42,13 @@ namespace
|
||||
|
||||
std::shared_ptr<ASTGrantQuery> current_query = nullptr;
|
||||
|
||||
for (const auto & element : grantee.access.getElements())
|
||||
AccessRightsElements elements;
|
||||
if (with_implicit)
|
||||
elements = ContextAccess::addImplicitAccessRights(grantee.access, *access_control).getElements();
|
||||
else
|
||||
elements = grantee.access.getElements();
|
||||
|
||||
for (const auto & element : elements)
|
||||
{
|
||||
if (element.empty())
|
||||
continue;
|
||||
@ -89,12 +96,13 @@ namespace
|
||||
ASTs getGrantQueriesImpl(
|
||||
const IAccessEntity & entity,
|
||||
const AccessControl * access_control /* not used if attach_mode == true */,
|
||||
bool attach_mode = false)
|
||||
bool attach_mode = false,
|
||||
bool with_implicit = false)
|
||||
{
|
||||
if (const User * user = typeid_cast<const User *>(&entity))
|
||||
return getGrantQueriesImpl(*user, access_control, attach_mode);
|
||||
return getGrantQueriesImpl(*user, access_control, attach_mode, with_implicit);
|
||||
if (const Role * role = typeid_cast<const Role *>(&entity))
|
||||
return getGrantQueriesImpl(*role, access_control, attach_mode);
|
||||
return getGrantQueriesImpl(*role, access_control, attach_mode, with_implicit);
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "{} is expected to be user or role", entity.formatTypeWithName());
|
||||
}
|
||||
|
||||
@ -180,23 +188,24 @@ ASTs InterpreterShowGrantsQuery::getGrantQueries() const
|
||||
auto entities = getEntities();
|
||||
const auto & access_control = getContext()->getAccessControl();
|
||||
|
||||
const auto & show_query = query_ptr->as<const ASTShowGrantsQuery &>();
|
||||
ASTs grant_queries;
|
||||
for (const auto & entity : entities)
|
||||
boost::range::push_back(grant_queries, getGrantQueries(*entity, access_control));
|
||||
boost::range::push_back(grant_queries, getGrantQueries(*entity, access_control, show_query.with_implicit));
|
||||
|
||||
return grant_queries;
|
||||
}
|
||||
|
||||
|
||||
ASTs InterpreterShowGrantsQuery::getGrantQueries(const IAccessEntity & user_or_role, const AccessControl & access_control)
|
||||
ASTs InterpreterShowGrantsQuery::getGrantQueries(const IAccessEntity & user_or_role, const AccessControl & access_control, bool with_implicit)
|
||||
{
|
||||
return getGrantQueriesImpl(user_or_role, &access_control, false);
|
||||
return getGrantQueriesImpl(user_or_role, &access_control, false, with_implicit);
|
||||
}
|
||||
|
||||
|
||||
ASTs InterpreterShowGrantsQuery::getAttachGrantQueries(const IAccessEntity & user_or_role)
|
||||
{
|
||||
return getGrantQueriesImpl(user_or_role, nullptr, true);
|
||||
return getGrantQueriesImpl(user_or_role, nullptr, true, false);
|
||||
}
|
||||
|
||||
void registerInterpreterShowGrantsQuery(InterpreterFactory & factory)
|
||||
|
@ -20,7 +20,7 @@ public:
|
||||
|
||||
BlockIO execute() override;
|
||||
|
||||
static ASTs getGrantQueries(const IAccessEntity & user_or_role, const AccessControl & access_control);
|
||||
static ASTs getGrantQueries(const IAccessEntity & user_or_role, const AccessControl & access_control, bool with_implicit = false);
|
||||
static ASTs getAttachGrantQueries(const IAccessEntity & user_or_role);
|
||||
|
||||
bool ignoreQuota() const override { return true; }
|
||||
|
@ -38,5 +38,11 @@ void ASTShowGrantsQuery::formatQueryImpl(const FormatSettings & settings, Format
|
||||
<< (settings.hilite ? hilite_none : "");
|
||||
for_roles->format(settings);
|
||||
}
|
||||
|
||||
if (with_implicit)
|
||||
{
|
||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << " WITH IMPLICIT"
|
||||
<< (settings.hilite ? hilite_none : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,13 @@ namespace DB
|
||||
{
|
||||
class ASTRolesOrUsersSet;
|
||||
|
||||
/** SHOW GRANTS [FOR user_name]
|
||||
/** SHOW GRANTS [FOR user1 [, user2 ...]] [WITH IMPLICIT]
|
||||
*/
|
||||
class ASTShowGrantsQuery : public ASTQueryWithOutput
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<ASTRolesOrUsersSet> for_roles;
|
||||
bool with_implicit = false;
|
||||
|
||||
String getID(char) const override;
|
||||
ASTPtr clone() const override;
|
||||
|
@ -31,8 +31,13 @@ bool ParserShowGrantsQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
||||
for_roles->current_user = true;
|
||||
}
|
||||
|
||||
bool with_implicit = false;
|
||||
if (ParserKeyword{Keyword::WITH_IMPLICIT}.ignore(pos, expected))
|
||||
with_implicit = true;
|
||||
|
||||
auto query = std::make_shared<ASTShowGrantsQuery>();
|
||||
query->for_roles = std::move(for_roles);
|
||||
query->with_implicit = with_implicit;
|
||||
node = query;
|
||||
|
||||
return true;
|
||||
|
@ -530,6 +530,7 @@ namespace DB
|
||||
MR_MACROS(WITH_NAME, "WITH NAME") \
|
||||
MR_MACROS(WITH_REPLACE_OPTION, "WITH REPLACE OPTION") \
|
||||
MR_MACROS(WITH_TIES, "WITH TIES") \
|
||||
MR_MACROS(WITH_IMPLICIT, "WITH IMPLICIT") \
|
||||
MR_MACROS(WITH, "WITH") \
|
||||
MR_MACROS(RECURSIVE, "RECURSIVE") \
|
||||
MR_MACROS(WK, "WK") \
|
||||
|
@ -0,0 +1,29 @@
|
||||
Empty grants
|
||||
GRANT SOURCES ON *.*
|
||||
GRANT TABLE ENGINE ON *
|
||||
GRANT SELECT ON system.aggregate_function_combinators
|
||||
GRANT SELECT ON system.collations
|
||||
GRANT SELECT ON system.columns
|
||||
GRANT SELECT ON system.contributors
|
||||
GRANT SELECT ON system.current_roles
|
||||
GRANT SELECT ON system.data_type_families
|
||||
GRANT SELECT ON system.database_engines
|
||||
GRANT SELECT ON system.databases
|
||||
GRANT SELECT ON system.enabled_roles
|
||||
GRANT SELECT ON system.formats
|
||||
GRANT SELECT ON system.functions
|
||||
GRANT SELECT ON system.licenses
|
||||
GRANT SELECT ON system.one
|
||||
GRANT SELECT ON system.privileges
|
||||
GRANT SELECT ON system.quota_usage
|
||||
GRANT SELECT ON system.settings
|
||||
GRANT SELECT ON system.table_engines
|
||||
GRANT SELECT ON system.table_functions
|
||||
GRANT SELECT ON system.tables
|
||||
GRANT SELECT ON system.time_zones
|
||||
Revoke grants
|
||||
GRANT SHOW DATABASES, SHOW TABLES, SHOW COLUMNS, SELECT, SOURCES ON *.*
|
||||
GRANT TABLE ENGINE ON *
|
||||
REVOKE SHOW TABLES, SHOW COLUMNS, SELECT ON test_03247.`table`
|
||||
OK
|
||||
0
|
25
tests/queries/0_stateless/03247_show_grants_with_implicit.sh
Executable file
25
tests/queries/0_stateless/03247_show_grants_with_implicit.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
# shellcheck source=../shell_config.sh
|
||||
. "$CURDIR"/../shell_config.sh
|
||||
|
||||
user="user03247_${CLICKHOUSE_DATABASE}_$RANDOM"
|
||||
|
||||
${CLICKHOUSE_CLIENT} --query "DROP USER IF EXISTS $user;";
|
||||
${CLICKHOUSE_CLIENT} --query "CREATE USER $user;";
|
||||
|
||||
${CLICKHOUSE_CLIENT} --query "GRANT SOURCES ON *.* TO $user;";
|
||||
|
||||
echo "Empty grants";
|
||||
${CLICKHOUSE_CLIENT} --query "SHOW GRANTS FOR $user WITH IMPLICIT;" | sed 's/ TO.*//';
|
||||
|
||||
echo "Revoke grants";
|
||||
${CLICKHOUSE_CLIENT} --query "GRANT SELECT ON *.* TO $user ;";
|
||||
${CLICKHOUSE_CLIENT} --query "REVOKE SELECT ON test_03247.table FROM $user;";
|
||||
${CLICKHOUSE_CLIENT} --query "SHOW GRANTS FOR $user WITH IMPLICIT;" | sed 's/ TO.*//' | sed 's/ FROM.*//';
|
||||
|
||||
(( $(${CLICKHOUSE_CLIENT} --user $user --query "EXISTS test_03247.table;" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED";
|
||||
${CLICKHOUSE_CLIENT} --query "EXISTS test_03247.table2;" --user $user;
|
||||
|
||||
${CLICKHOUSE_CLIENT} --query "DROP USER IF EXISTS $user;";
|
Loading…
Reference in New Issue
Block a user