提交 37ce6e26 编写于 作者: V Vitaly Baranov

Add a new clause GRANTEES to commands CREATE USER and ALTER USER.

上级 d6e0342c
......@@ -11,7 +11,7 @@ bool User::equal(const IAccessEntity & other) const
const auto & other_user = typeid_cast<const User &>(other);
return (authentication == other_user.authentication) && (allowed_client_hosts == other_user.allowed_client_hosts)
&& (access == other_user.access) && (granted_roles == other_user.granted_roles) && (default_roles == other_user.default_roles)
&& (settings == other_user.settings);
&& (settings == other_user.settings) && (grantees == other_user.grantees);
}
}
......@@ -21,6 +21,7 @@ struct User : public IAccessEntity
GrantedRoles granted_roles;
RolesOrUsersSet default_roles = RolesOrUsersSet::AllTag{};
SettingsProfileElements settings;
RolesOrUsersSet grantees = RolesOrUsersSet::AllTag{};
bool equal(const IAccessEntity & other) const override;
std::shared_ptr<IAccessEntity> clone() const override { return cloneImpl<User>(); }
......
......@@ -20,7 +20,8 @@ namespace
const ASTCreateUserQuery & query,
const std::shared_ptr<ASTUserNameWithHost> & override_name,
const std::optional<RolesOrUsersSet> & override_default_roles,
const std::optional<SettingsProfileElements> & override_settings)
const std::optional<SettingsProfileElements> & override_settings,
const std::optional<RolesOrUsersSet> & override_grantees)
{
if (override_name)
user.setName(override_name->toString());
......@@ -62,6 +63,11 @@ namespace
user.settings = *override_settings;
else if (query.settings)
user.settings = *query.settings;
if (override_grantees)
user.grantees = *override_grantees;
else if (query.grantees)
user.grantees = *query.grantees;
}
}
......@@ -93,12 +99,17 @@ BlockIO InterpreterCreateUserQuery::execute()
if (query.alter)
{
std::optional<RolesOrUsersSet> grantees_from_query;
if (query.grantees)
grantees_from_query = RolesOrUsersSet{*query.grantees, access_control};
auto update_func = [&](const AccessEntityPtr & entity) -> AccessEntityPtr
{
auto updated_user = typeid_cast<std::shared_ptr<User>>(entity->clone());
updateUserFromQueryImpl(*updated_user, query, {}, default_roles_from_query, settings_from_query);
updateUserFromQueryImpl(*updated_user, query, {}, default_roles_from_query, settings_from_query, grantees_from_query);
return updated_user;
};
Strings names = query.names->toStrings();
if (query.if_exists)
{
......@@ -114,16 +125,28 @@ BlockIO InterpreterCreateUserQuery::execute()
for (const auto & name : *query.names)
{
auto new_user = std::make_shared<User>();
updateUserFromQueryImpl(*new_user, query, name, default_roles_from_query, settings_from_query);
updateUserFromQueryImpl(*new_user, query, name, default_roles_from_query, settings_from_query, RolesOrUsersSet::AllTag{});
new_users.emplace_back(std::move(new_user));
}
std::vector<UUID> ids;
if (query.if_not_exists)
access_control.tryInsert(new_users);
ids = access_control.tryInsert(new_users);
else if (query.or_replace)
access_control.insertOrReplace(new_users);
ids = access_control.insertOrReplace(new_users);
else
access_control.insert(new_users);
ids = access_control.insert(new_users);
if (query.grantees)
{
RolesOrUsersSet grantees_from_query = RolesOrUsersSet{*query.grantees, access_control};
access_control.update(ids, [&](const AccessEntityPtr & entity) -> AccessEntityPtr
{
auto updated_user = typeid_cast<std::shared_ptr<User>>(entity->clone());
updated_user->grantees = grantees_from_query;
return updated_user;
});
}
}
return {};
......@@ -132,7 +155,7 @@ BlockIO InterpreterCreateUserQuery::execute()
void InterpreterCreateUserQuery::updateUserFromQuery(User & user, const ASTCreateUserQuery & query)
{
updateUserFromQueryImpl(user, query, {}, {}, {});
updateUserFromQueryImpl(user, query, {}, {}, {}, {});
}
}
......@@ -16,6 +16,7 @@ namespace DB
{
namespace ErrorCodes
{
extern const int ACCESS_DENIED;
extern const int LOGICAL_ERROR;
}
......@@ -65,6 +66,29 @@ namespace
updateFromQueryTemplate(*role, query, roles_to_grant_or_revoke);
}
void checkGranteeIsAllowed(const ContextAccess & access, const UUID & grantee_id, const IAccessEntity & grantee)
{
auto current_user = access.getUser();
if (current_user && !current_user->grantees.match(grantee_id))
throw Exception(grantee.outputTypeAndName() + " is not allowed as grantee", ErrorCodes::ACCESS_DENIED);
}
void checkGranteesAreAllowed(const AccessControlManager & access_control, const ContextAccess & access, const std::vector<UUID> & grantee_ids)
{
auto current_user = access.getUser();
if (!current_user || (current_user->grantees == RolesOrUsersSet::AllTag{}))
return;
for (const auto & id : grantee_ids)
{
auto entity = access_control.tryRead(id);
if (auto role = typeid_cast<RolePtr>(entity))
checkGranteeIsAllowed(access, id, *role);
else if (auto user = typeid_cast<UserPtr>(entity))
checkGranteeIsAllowed(access, id, *user);
}
}
void checkGrantOption(
const AccessControlManager & access_control,
const ContextAccess & access,
......@@ -80,11 +104,15 @@ namespace
if (!query.is_revoke)
{
access.checkGrantOption(elements);
checkGranteesAreAllowed(access_control, access, grantees_from_query);
return;
}
if (access.hasGrantOption(elements))
{
checkGranteesAreAllowed(access_control, access, grantees_from_query);
return;
}
/// Special case for the command REVOKE: it's possible that the current user doesn't have
/// the access granted with GRANT OPTION but it's still ok because the roles or users
......@@ -99,9 +127,15 @@ namespace
{
auto entity = access_control.tryRead(id);
if (auto role = typeid_cast<RolePtr>(entity))
{
checkGranteeIsAllowed(access, id, *role);
all_granted_access.makeUnion(role->access);
}
else if (auto user = typeid_cast<UserPtr>(entity))
{
checkGranteeIsAllowed(access, id, *user);
all_granted_access.makeUnion(user->access);
}
}
AccessRights required_access;
......@@ -138,6 +172,7 @@ namespace
{
matching_ids = roles_from_query.getMatchingIDs(access_control);
access.checkAdminOption(matching_ids);
checkGranteesAreAllowed(access_control, access, grantees_from_query);
return matching_ids;
}
......@@ -145,7 +180,10 @@ namespace
{
matching_ids = roles_from_query.getMatchingIDs();
if (access.hasAdminOption(matching_ids))
{
checkGranteesAreAllowed(access_control, access, grantees_from_query);
return matching_ids;
}
}
/// Special case for the command REVOKE: it's possible that the current user doesn't have the admin option
......@@ -161,9 +199,15 @@ namespace
{
auto entity = access_control.tryRead(id);
if (auto role = typeid_cast<RolePtr>(entity))
{
checkGranteeIsAllowed(access, id, *role);
all_granted_roles.makeUnion(role->granted_roles);
}
else if (auto user = typeid_cast<UserPtr>(entity))
{
checkGranteeIsAllowed(access, id, *user);
all_granted_roles.makeUnion(user->granted_roles);
}
}
const auto & all_granted_roles_set = query.admin_option ? all_granted_roles.getGrantedWithAdminOption() : all_granted_roles.getGranted();
......@@ -206,6 +250,7 @@ BlockIO InterpreterGrantQuery::execute()
/// To execute the command GRANT the current user needs to have the access granted with GRANT OPTION.
auto required_access = query.access_rights_elements;
std::for_each(required_access.begin(), required_access.end(), [&](AccessRightsElement & element) { element.grant_option = true; });
checkGranteesAreAllowed(access_control, *context.getAccess(), grantees);
return executeDDLQueryOnCluster(query_ptr, context, std::move(required_access));
}
......
......@@ -73,6 +73,15 @@ namespace
query->settings = user.settings.toASTWithNames(*manager);
}
if (user.grantees != RolesOrUsersSet::AllTag{})
{
if (attach_mode)
query->grantees = user.grantees.toAST();
else
query->grantees = user.grantees.toASTWithNames(*manager);
query->grantees->use_keyword_any = true;
}
return query;
}
......
......@@ -203,6 +203,13 @@ namespace
format.ostr << (format.hilite ? IAST::hilite_keyword : "") << " SETTINGS " << (format.hilite ? IAST::hilite_none : "");
settings.format(format);
}
void formatGrantees(const ASTRolesOrUsersSet & grantees, const IAST::FormatSettings & settings)
{
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " GRANTEES " << (settings.hilite ? IAST::hilite_none : "");
grantees.format(settings);
}
}
......@@ -260,5 +267,8 @@ void ASTCreateUserQuery::formatImpl(const FormatSettings & format, FormatState &
if (settings && (!settings->empty() || alter))
formatSettings(*settings, format);
if (grantees)
formatGrantees(*grantees, format);
}
}
......@@ -17,6 +17,7 @@ class ASTSettingsProfileElements;
* [HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
* [DEFAULT ROLE role [,...]]
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
* [GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]]
*
* ALTER USER [IF EXISTS] name
* [RENAME TO new_name]
......@@ -24,6 +25,7 @@ class ASTSettingsProfileElements;
* [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
* [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
* [GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]]
*/
class ASTCreateUserQuery : public IAST, public ASTQueryWithOnCluster
{
......@@ -47,6 +49,7 @@ public:
std::shared_ptr<ASTRolesOrUsersSet> default_roles;
std::shared_ptr<ASTSettingsProfileElements> settings;
std::shared_ptr<ASTRolesOrUsersSet> grantees;
String getID(char) const override;
ASTPtr clone() const override;
......
......@@ -35,7 +35,8 @@ void ASTRolesOrUsersSet::formatImpl(const FormatSettings & settings, FormatState
{
if (std::exchange(need_comma, true))
settings.ostr << ", ";
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << "ALL" << (settings.hilite ? IAST::hilite_none : "");
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << (use_keyword_any ? "ANY" : "ALL")
<< (settings.hilite ? IAST::hilite_none : "");
}
else
{
......
......@@ -20,9 +20,10 @@ public:
Strings except_names;
bool except_current_user = false;
bool allow_users = true; /// whether this set can contain names of users
bool allow_roles = true; /// whether this set can contain names of roles
bool id_mode = false; /// whether this set keep UUIDs instead of names
bool allow_users = true; /// whether this set can contain names of users
bool allow_roles = true; /// whether this set can contain names of roles
bool id_mode = false; /// whether this set keep UUIDs instead of names
bool use_keyword_any = false; /// whether the keyword ANY should be used instead of the keyword ALL
bool empty() const { return names.empty() && !current_user && !all; }
void replaceCurrentUserTag(const String & current_user_name);
......
......@@ -275,6 +275,24 @@ namespace
});
}
bool parseGrantees(IParserBase::Pos & pos, Expected & expected, bool id_mode, std::shared_ptr<ASTRolesOrUsersSet> & grantees)
{
return IParserBase::wrapParseImpl(pos, [&]
{
if (!ParserKeyword{"GRANTEES"}.ignore(pos, expected))
return false;
ASTPtr ast;
ParserRolesOrUsersSet grantees_p;
grantees_p.allowAny().allowUsers().allowCurrentUser().allowRoles().useIDMode(id_mode);
if (!grantees_p.parse(pos, ast, expected))
return false;
grantees = typeid_cast<std::shared_ptr<ASTRolesOrUsersSet>>(ast);
return true;
});
}
bool parseOnCluster(IParserBase::Pos & pos, Expected & expected, String & cluster)
{
return IParserBase::wrapParseImpl(pos, [&]
......@@ -330,6 +348,7 @@ bool ParserCreateUserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
std::optional<AllowedClientHosts> remove_hosts;
std::shared_ptr<ASTRolesOrUsersSet> default_roles;
std::shared_ptr<ASTSettingsProfileElements> settings;
std::shared_ptr<ASTRolesOrUsersSet> grantees;
String cluster;
while (true)
......@@ -368,6 +387,9 @@ bool ParserCreateUserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
if (cluster.empty() && parseOnCluster(pos, expected, cluster))
continue;
if (!grantees && parseGrantees(pos, expected, attach_mode, grantees))
continue;
if (alter)
{
if (new_name.empty() && (names->size() == 1) && parseRenameTo(pos, expected, new_name))
......@@ -422,6 +444,7 @@ bool ParserCreateUserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
query->remove_hosts = std::move(remove_hosts);
query->default_roles = std::move(default_roles);
query->settings = std::move(settings);
query->grantees = std::move(grantees);
return true;
}
......
......@@ -11,6 +11,7 @@ namespace DB
* [HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
* [DEFAULT ROLE role [,...]]
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
* [GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]]
*
* ALTER USER [IF EXISTS] name
* [RENAME TO new_name]
......@@ -18,6 +19,7 @@ namespace DB
* [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
* [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
* [GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]]
*/
class ParserCreateUserQuery : public IParserBase
{
......
......@@ -40,6 +40,7 @@ namespace
Expected & expected,
bool id_mode,
bool allow_all,
bool allow_any,
bool allow_current_user,
bool & all,
Strings & names,
......@@ -61,6 +62,12 @@ namespace
return true;
}
if (allow_any && ParserKeyword{"ANY"}.ignore(pos, expected))
{
res_all = true;
return true;
}
if (allow_current_user && parseCurrentUserTag(pos, expected))
{
res_current_user = true;
......@@ -99,7 +106,7 @@ namespace
return false;
bool unused;
return parseBeforeExcept(pos, expected, id_mode, false, allow_current_user, unused, except_names, except_current_user);
return parseBeforeExcept(pos, expected, id_mode, false, false, allow_current_user, unused, except_names, except_current_user);
});
}
}
......@@ -113,7 +120,7 @@ bool ParserRolesOrUsersSet::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
Strings except_names;
bool except_current_user = false;
if (!parseBeforeExcept(pos, expected, id_mode, allow_all, allow_current_user, all, names, current_user))
if (!parseBeforeExcept(pos, expected, id_mode, allow_all, allow_any, allow_current_user, all, names, current_user))
return false;
parseExceptAndAfterExcept(pos, expected, id_mode, allow_current_user, except_names, except_current_user);
......@@ -130,6 +137,7 @@ bool ParserRolesOrUsersSet::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
result->allow_users = allow_users;
result->allow_roles = allow_roles;
result->id_mode = id_mode;
result->use_keyword_any = all && allow_any && !allow_all;
node = result;
return true;
}
......
......@@ -13,6 +13,7 @@ class ParserRolesOrUsersSet : public IParserBase
{
public:
ParserRolesOrUsersSet & allowAll(bool allow_all_ = true) { allow_all = allow_all_; return *this; }
ParserRolesOrUsersSet & allowAny(bool allow_any_ = true) { allow_any = allow_any_; return *this; }
ParserRolesOrUsersSet & allowUsers(bool allow_users_ = true) { allow_users = allow_users_; return *this; }
ParserRolesOrUsersSet & allowCurrentUser(bool allow_current_user_ = true) { allow_current_user = allow_current_user_; return *this; }
ParserRolesOrUsersSet & allowRoles(bool allow_roles_ = true) { allow_roles = allow_roles_; return *this; }
......@@ -24,6 +25,7 @@ protected:
private:
bool allow_all = false;
bool allow_any = false;
bool allow_users = false;
bool allow_current_user = false;
bool allow_roles = false;
......
......@@ -47,6 +47,9 @@ NamesAndTypesList StorageSystemUsers::getNamesAndTypes()
{"default_roles_all", std::make_shared<DataTypeUInt8>()},
{"default_roles_list", std::make_shared<DataTypeArray>(std::make_shared<DataTypeString>())},
{"default_roles_except", std::make_shared<DataTypeArray>(std::make_shared<DataTypeString>())},
{"grantees_any", std::make_shared<DataTypeUInt8>()},
{"grantees_list", std::make_shared<DataTypeArray>(std::make_shared<DataTypeString>())},
{"grantees_except", std::make_shared<DataTypeArray>(std::make_shared<DataTypeString>())},
};
return names_and_types;
}
......@@ -77,13 +80,19 @@ void StorageSystemUsers::fillData(MutableColumns & res_columns, const Context &
auto & column_default_roles_list_offsets = assert_cast<ColumnArray &>(*res_columns[column_index++]).getOffsets();
auto & column_default_roles_except = assert_cast<ColumnString &>(assert_cast<ColumnArray &>(*res_columns[column_index]).getData());
auto & column_default_roles_except_offsets = assert_cast<ColumnArray &>(*res_columns[column_index++]).getOffsets();
auto & column_grantees_any = assert_cast<ColumnUInt8 &>(*res_columns[column_index++]).getData();
auto & column_grantees_list = assert_cast<ColumnString &>(assert_cast<ColumnArray &>(*res_columns[column_index]).getData());
auto & column_grantees_list_offsets = assert_cast<ColumnArray &>(*res_columns[column_index++]).getOffsets();
auto & column_grantees_except = assert_cast<ColumnString &>(assert_cast<ColumnArray &>(*res_columns[column_index]).getData());
auto & column_grantees_except_offsets = assert_cast<ColumnArray &>(*res_columns[column_index++]).getOffsets();
auto add_row = [&](const String & name,
const UUID & id,
const String & storage_name,
const Authentication & authentication,
const AllowedClientHosts & allowed_hosts,
const RolesOrUsersSet & default_roles)
const RolesOrUsersSet & default_roles,
const RolesOrUsersSet & grantees)
{
column_name.insertData(name.data(), name.length());
column_id.push_back(id);
......@@ -156,14 +165,21 @@ void StorageSystemUsers::fillData(MutableColumns & res_columns, const Context &
auto default_roles_ast = default_roles.toASTWithNames(access_control);
column_default_roles_all.push_back(default_roles_ast->all);
for (const auto & role_name : default_roles_ast->names)
column_default_roles_list.insertData(role_name.data(), role_name.length());
column_default_roles_list_offsets.push_back(column_default_roles_list.size());
for (const auto & role_name : default_roles_ast->except_names)
column_default_roles_except.insertData(role_name.data(), role_name.length());
for (const auto & except_name : default_roles_ast->except_names)
column_default_roles_except.insertData(except_name.data(), except_name.length());
column_default_roles_except_offsets.push_back(column_default_roles_except.size());
auto grantees_ast = grantees.toASTWithNames(access_control);
column_grantees_any.push_back(grantees_ast->all);
for (const auto & grantee_name : grantees_ast->names)
column_grantees_list.insertData(grantee_name.data(), grantee_name.length());
column_grantees_list_offsets.push_back(column_grantees_list.size());
for (const auto & except_name : grantees_ast->except_names)
column_grantees_except.insertData(except_name.data(), except_name.length());
column_grantees_except_offsets.push_back(column_grantees_except.size());
};
for (const auto & id : ids)
......@@ -176,7 +192,7 @@ void StorageSystemUsers::fillData(MutableColumns & res_columns, const Context &
if (!storage)
continue;
add_row(user->getName(), id, storage->getStorageName(), user->authentication, user->allowed_client_hosts, user->default_roles);
add_row(user->getName(), id, storage->getStorageName(), user->authentication, user->allowed_client_hosts, user->default_roles, user->grantees);
}
}
......
......@@ -26,7 +26,7 @@ def cleanup_after_test():
try:
yield
finally:
instance.query("DROP USER IF EXISTS A, B")
instance.query("DROP USER IF EXISTS A, B, C")
instance.query("DROP TABLE IF EXISTS test.view_1")
......@@ -106,6 +106,46 @@ def test_revoke_requires_grant_option():
assert instance.query("SHOW GRANTS FOR B") == ""
def test_allowed_grantees():
instance.query("CREATE USER A")
instance.query("CREATE USER B")
instance.query('GRANT SELECT ON test.table TO A WITH GRANT OPTION')
instance.query("GRANT SELECT ON test.table TO B", user='A')
assert instance.query("SELECT * FROM test.table", user='B') == "1\t5\n2\t10\n"
instance.query("REVOKE SELECT ON test.table FROM B", user='A')
instance.query('ALTER USER A GRANTEES NONE')
expected_error = "user `B` is not allowed as grantee"
assert expected_error in instance.query_and_get_error("GRANT SELECT ON test.table TO B", user='A')
instance.query('ALTER USER A GRANTEES ANY EXCEPT B')
assert instance.query('SHOW CREATE USER A') == "CREATE USER A GRANTEES ANY EXCEPT B\n"
expected_error = "user `B` is not allowed as grantee"
assert expected_error in instance.query_and_get_error("GRANT SELECT ON test.table TO B", user='A')
instance.query('ALTER USER A GRANTEES B')
instance.query("GRANT SELECT ON test.table TO B", user='A')
assert instance.query("SELECT * FROM test.table", user='B') == "1\t5\n2\t10\n"
instance.query("REVOKE SELECT ON test.table FROM B", user='A')
instance.query('ALTER USER A GRANTEES ANY')
assert instance.query('SHOW CREATE USER A') == "CREATE USER A\n"
instance.query("GRANT SELECT ON test.table TO B", user='A')
assert instance.query("SELECT * FROM test.table", user='B') == "1\t5\n2\t10\n"
instance.query('ALTER USER A GRANTEES NONE')
expected_error = "user `B` is not allowed as grantee"
assert expected_error in instance.query_and_get_error("REVOKE SELECT ON test.table FROM B", user='A')
instance.query("CREATE USER C GRANTEES ANY EXCEPT C")
assert instance.query('SHOW CREATE USER C') == "CREATE USER C GRANTEES ANY EXCEPT C\n"
instance.query('GRANT SELECT ON test.table TO C WITH GRANT OPTION')
assert instance.query("SELECT * FROM test.table", user='C') == "1\t5\n2\t10\n"
expected_error = "user `C` is not allowed as grantee"
assert expected_error in instance.query_and_get_error("REVOKE SELECT ON test.table FROM C", user='C')
def test_grant_all_on_table():
instance.query("CREATE USER A, B")
instance.query("GRANT ALL ON test.table TO A WITH GRANT OPTION")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册