Skip to content

Commit

Permalink
Add DROP ROLE ... FROM and CREATE ROLE ... AT syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
pufit committed Jul 11, 2023
1 parent d3a0e48 commit 77e45e5
Show file tree
Hide file tree
Showing 18 changed files with 211 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/Access/AccessControl.cpp
Expand Up @@ -415,7 +415,7 @@ void AccessControl::addStoragesFromUserDirectoriesConfig(
String type = key_in_user_directories;
if (size_t bracket_pos = type.find('['); bracket_pos != String::npos)
type.resize(bracket_pos);
if ((type == "users_xml") || (type == "users_config"))
if ((type == "users.xml") || (type == "users_config"))
type = UsersConfigAccessStorage::STORAGE_TYPE;
else if ((type == "local") || (type == "local_directory"))
type = DiskAccessStorage::STORAGE_TYPE;
Expand Down
2 changes: 1 addition & 1 deletion src/Access/DiskAccessStorage.h
Expand Up @@ -13,7 +13,7 @@ class AccessChangesNotifier;
class DiskAccessStorage : public IAccessStorage
{
public:
static constexpr char STORAGE_TYPE[] = "local directory";
static constexpr char STORAGE_TYPE[] = "local_directory";

DiskAccessStorage(const String & storage_name_, const String & directory_path_, AccessChangesNotifier & changes_notifier_, bool readonly_, bool allow_backup_);
~DiskAccessStorage() override;
Expand Down
35 changes: 35 additions & 0 deletions src/Access/MultipleAccessStorage.cpp
Expand Up @@ -16,6 +16,7 @@ namespace ErrorCodes
{
extern const int ACCESS_ENTITY_ALREADY_EXISTS;
extern const int ACCESS_STORAGE_FOR_INSERTION_NOT_FOUND;
extern const int ACCESS_ENTITY_NOT_FOUND;
}

using Storage = IAccessStorage;
Expand Down Expand Up @@ -178,6 +179,40 @@ ConstStoragePtr MultipleAccessStorage::getStorage(const UUID & id) const
return const_cast<MultipleAccessStorage *>(this)->getStorage(id);
}

StoragePtr MultipleAccessStorage::findStorageByName(const DB::String & storage_name)
{
auto storages = getStoragesInternal();
for (const auto & storage : *storages)
{
if (storage->getStorageName() == storage_name)
return storage;
}

return nullptr;
}


ConstStoragePtr MultipleAccessStorage::findStorageByName(const DB::String & storage_name) const
{
return const_cast<MultipleAccessStorage *>(this)->findStorageByName(storage_name);
}


StoragePtr MultipleAccessStorage::getStorageByName(const DB::String & storage_name)
{
auto storage = findStorageByName(storage_name);
if (storage)
return storage;

throw Exception(ErrorCodes::ACCESS_ENTITY_NOT_FOUND, "Access storage with name {} is not found", storage_name);
}


ConstStoragePtr MultipleAccessStorage::getStorageByName(const DB::String & storage_name) const
{
return const_cast<MultipleAccessStorage *>(this)->getStorageByName(storage_name);
}

AccessEntityPtr MultipleAccessStorage::readImpl(const UUID & id, bool throw_if_not_exists) const
{
if (auto storage = findStorage(id))
Expand Down
5 changes: 5 additions & 0 deletions src/Access/MultipleAccessStorage.h
Expand Up @@ -41,6 +41,11 @@ class MultipleAccessStorage : public IAccessStorage
ConstStoragePtr getStorage(const UUID & id) const;
StoragePtr getStorage(const UUID & id);

ConstStoragePtr findStorageByName(const String & storage_name) const;
StoragePtr findStorageByName(const String & storage_name);
ConstStoragePtr getStorageByName(const String & storage_name) const;
StoragePtr getStorageByName(const String & storage_name);

bool exists(const UUID & id) const override;

bool isBackupAllowed() const override;
Expand Down
2 changes: 1 addition & 1 deletion src/Access/UsersConfigAccessStorage.h
Expand Up @@ -20,7 +20,7 @@ class UsersConfigAccessStorage : public IAccessStorage
{
public:

static constexpr char STORAGE_TYPE[] = "users.xml";
static constexpr char STORAGE_TYPE[] = "users_xml";

UsersConfigAccessStorage(const String & storage_name_, AccessControl & access_control_, bool allow_backup_);
~UsersConfigAccessStorage() override;
Expand Down
42 changes: 36 additions & 6 deletions src/Interpreters/Access/InterpreterCreateRoleQuery.cpp
Expand Up @@ -8,6 +8,12 @@

namespace DB
{

namespace ErrorCodes
{
extern const int ACCESS_ENTITY_ALREADY_EXISTS;
}

namespace
{
void updateRoleFromQueryImpl(
Expand Down Expand Up @@ -52,6 +58,15 @@ BlockIO InterpreterCreateRoleQuery::execute()
if (!query.cluster.empty())
return executeDDLQueryOnCluster(query_ptr, getContext());

IAccessStorage * storage = &access_control;
MultipleAccessStorage::StoragePtr storage_ptr;

if (!query.storage_name.empty())
{
storage_ptr = access_control.getStorageByName(query.storage_name);
storage = storage_ptr.get();
}

if (query.alter)
{
auto update_func = [&](const AccessEntityPtr & entity) -> AccessEntityPtr
Expand All @@ -62,11 +77,11 @@ BlockIO InterpreterCreateRoleQuery::execute()
};
if (query.if_exists)
{
auto ids = access_control.find<Role>(query.names);
access_control.tryUpdate(ids, update_func);
auto ids = storage->find<Role>(query.names);
storage->tryUpdate(ids, update_func);
}
else
access_control.update(access_control.getIDs<Role>(query.names), update_func);
storage->update(storage->getIDs<Role>(query.names), update_func);
}
else
{
Expand All @@ -78,12 +93,27 @@ BlockIO InterpreterCreateRoleQuery::execute()
new_roles.emplace_back(std::move(new_role));
}

if (!query.storage_name.empty())
{
for (const auto & name : query.names)
{
auto id = access_control.find<Role>(name);

if (!id)
continue;

auto another_storage_ptr = access_control.findStorage(*id);
if (another_storage_ptr != storage_ptr)
throw Exception(ErrorCodes::ACCESS_ENTITY_ALREADY_EXISTS, "Role {} already exists in storage {}", name, another_storage_ptr->getStorageName());
}
}

if (query.if_not_exists)
access_control.tryInsert(new_roles);
storage->tryInsert(new_roles);
else if (query.or_replace)
access_control.insertOrReplace(new_roles);
storage->insertOrReplace(new_roles);
else
access_control.insert(new_roles);
storage->insert(new_roles);
}

return {};
Expand Down
18 changes: 13 additions & 5 deletions src/Interpreters/Access/InterpreterDropAccessEntityQuery.cpp
Expand Up @@ -26,18 +26,26 @@ BlockIO InterpreterDropAccessEntityQuery::execute()

query.replaceEmptyDatabase(getContext()->getCurrentDatabase());

auto do_drop = [&](const Strings & names)
auto do_drop = [&](const Strings & names, const String & storage_name)
{
IAccessStorage * storage = &access_control;
MultipleAccessStorage::StoragePtr storage_ptr;
if (!storage_name.empty())
{
storage_ptr = access_control.getStorageByName(storage_name);
storage = storage_ptr.get();
}

if (query.if_exists)
access_control.tryRemove(access_control.find(query.type, names));
storage->tryRemove(storage->find(query.type, names));
else
access_control.remove(access_control.getIDs(query.type, names));
storage->remove(storage->getIDs(query.type, names));
};

if (query.type == AccessEntityType::ROW_POLICY)
do_drop(query.row_policy_names->toStrings());
do_drop(query.row_policy_names->toStrings(), query.storage_name);
else
do_drop(query.names);
do_drop(query.names, query.storage_name);

return {};
}
Expand Down
6 changes: 6 additions & 0 deletions src/Parsers/Access/ASTCreateRoleQuery.cpp
Expand Up @@ -71,6 +71,12 @@ void ASTCreateRoleQuery::formatImpl(const FormatSettings & format, FormatState &
format.ostr << (format.hilite ? hilite_keyword : "") << " OR REPLACE" << (format.hilite ? hilite_none : "");

formatNames(names, format);

if (!storage_name.empty())
format.ostr << (format.hilite ? IAST::hilite_keyword : "")
<< " AT " << (format.hilite ? IAST::hilite_none : "")
<< backQuoteIfNeed(storage_name);

formatOnCluster(format);

if (!new_name.empty())
Expand Down
1 change: 1 addition & 0 deletions src/Parsers/Access/ASTCreateRoleQuery.h
Expand Up @@ -28,6 +28,7 @@ class ASTCreateRoleQuery : public IAST, public ASTQueryWithOnCluster

Strings names;
String new_name;
String storage_name;

std::shared_ptr<ASTSettingsProfileElements> settings;

Expand Down
5 changes: 5 additions & 0 deletions src/Parsers/Access/ASTDropAccessEntityQuery.cpp
Expand Up @@ -53,6 +53,11 @@ void ASTDropAccessEntityQuery::formatImpl(const FormatSettings & settings, Forma
else
formatNames(names, settings);

if (!storage_name.empty())
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "")
<< " FROM " << (settings.hilite ? IAST::hilite_none : "")
<< backQuoteIfNeed(storage_name);

formatOnCluster(settings);
}

Expand Down
1 change: 1 addition & 0 deletions src/Parsers/Access/ASTDropAccessEntityQuery.h
Expand Up @@ -21,6 +21,7 @@ class ASTDropAccessEntityQuery : public IAST, public ASTQueryWithOnCluster
AccessEntityType type;
bool if_exists = false;
Strings names;
String storage_name;
std::shared_ptr<ASTRowPolicyNames> row_policy_names;

String getID(char) const override;
Expand Down
5 changes: 5 additions & 0 deletions src/Parsers/Access/ParserCreateRoleQuery.cpp
Expand Up @@ -91,6 +91,10 @@ bool ParserCreateRoleQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
String new_name;
std::shared_ptr<ASTSettingsProfileElements> settings;
String cluster;
String storage_name;

if (ParserKeyword{"AT"}.ignore(pos, expected))
parseStorageName(pos, expected, storage_name);

while (true)
{
Expand Down Expand Up @@ -125,6 +129,7 @@ bool ParserCreateRoleQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
query->names = std::move(names);
query->new_name = std::move(new_name);
query->settings = std::move(settings);
query->storage_name = std::move(storage_name);

return true;
}
Expand Down
5 changes: 5 additions & 0 deletions src/Parsers/Access/ParserDropAccessEntityQuery.cpp
Expand Up @@ -53,6 +53,7 @@ bool ParserDropAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expected &

Strings names;
std::shared_ptr<ASTRowPolicyNames> row_policy_names;
String storage_name;
String cluster;

if ((type == AccessEntityType::USER) || (type == AccessEntityType::ROLE))
Expand All @@ -76,6 +77,9 @@ bool ParserDropAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expected &
return false;
}

if (ParserKeyword{"FROM"}.ignore(pos, expected))
parseStorageName(pos, expected, storage_name);

if (cluster.empty())
parseOnCluster(pos, expected, cluster);

Expand All @@ -87,6 +91,7 @@ bool ParserDropAccessEntityQuery::parseImpl(Pos & pos, ASTPtr & node, Expected &
query->cluster = std::move(cluster);
query->names = std::move(names);
query->row_policy_names = std::move(row_policy_names);
query->storage_name = std::move(storage_name);

return true;
}
Expand Down
5 changes: 5 additions & 0 deletions src/Parsers/Access/parseUserName.h
Expand Up @@ -34,4 +34,9 @@ inline bool parseRoleNames(IParser::Pos & pos, Expected & expected, Strings & ro
return parseUserNames(pos, expected, role_names);
}

inline bool parseStorageName(IParser::Pos & pos, Expected & expected, String & storage_name)
{
return parseUserName(pos, expected, storage_name);
}

}
7 changes: 6 additions & 1 deletion src/Storages/System/StorageSystemRoleGrants.cpp
Expand Up @@ -3,6 +3,7 @@
#include <DataTypes/DataTypeNullable.h>
#include <DataTypes/DataTypeString.h>
#include <DataTypes/DataTypesNumber.h>
#include <DataTypes/DataTypeUUID.h>
#include <Columns/ColumnString.h>
#include <Columns/ColumnNullable.h>
#include <Columns/ColumnsNumber.h>
Expand All @@ -22,6 +23,7 @@ NamesAndTypesList StorageSystemRoleGrants::getNamesAndTypes()
{"user_name", std::make_shared<DataTypeNullable>(std::make_shared<DataTypeString>())},
{"role_name", std::make_shared<DataTypeNullable>(std::make_shared<DataTypeString>())},
{"granted_role_name", std::make_shared<DataTypeString>()},
{"granted_role_id", std::make_shared<DataTypeUUID>()},
{"granted_role_is_default", std::make_shared<DataTypeUInt8>()},
{"with_admin_option", std::make_shared<DataTypeUInt8>()},
};
Expand All @@ -45,12 +47,14 @@ void StorageSystemRoleGrants::fillData(MutableColumns & res_columns, ContextPtr
auto & column_role_name = assert_cast<ColumnString &>(assert_cast<ColumnNullable &>(*res_columns[column_index]).getNestedColumn());
auto & column_role_name_null_map = assert_cast<ColumnNullable &>(*res_columns[column_index++]).getNullMapData();
auto & column_granted_role_name = assert_cast<ColumnString &>(*res_columns[column_index++]);
auto & column_granted_role_id = assert_cast<ColumnUUID &>(*res_columns[column_index++]).getData();
auto & column_is_default = assert_cast<ColumnUInt8 &>(*res_columns[column_index++]).getData();
auto & column_admin_option = assert_cast<ColumnUInt8 &>(*res_columns[column_index++]).getData();

auto add_row = [&](const String & grantee_name,
AccessEntityType grantee_type,
const String & granted_role_name,
const UUID & granted_role_id,
bool is_default,
bool with_admin_option)
{
Expand All @@ -72,6 +76,7 @@ void StorageSystemRoleGrants::fillData(MutableColumns & res_columns, ContextPtr
assert(false);

column_granted_role_name.insertData(granted_role_name.data(), granted_role_name.length());
column_granted_role_id.push_back(granted_role_id.toUnderType());
column_is_default.push_back(is_default);
column_admin_option.push_back(with_admin_option);
};
Expand All @@ -90,7 +95,7 @@ void StorageSystemRoleGrants::fillData(MutableColumns & res_columns, ContextPtr
continue;

bool is_default = !default_roles || default_roles->match(role_id);
add_row(grantee_name, grantee_type, *role_name, is_default, element.admin_option);
add_row(grantee_name, grantee_type, *role_name, role_id, is_default, element.admin_option);
}
}
};
Expand Down
Empty file.
@@ -0,0 +1,13 @@
<clickhouse>
<user_directories>
<memory/>
</user_directories>

<roles>
<default_role>
<grants>
<query>GRANT ALL ON *.* WITH GRANT OPTION</query>
</grants>
</default_role>
</roles>
</clickhouse>

0 comments on commit 77e45e5

Please sign in to comment.