mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-20 00:30:49 +00:00
Add support for setting 'allow_backup' to skip access entities from putting to backup.
This commit is contained in:
parent
01aaaf7395
commit
9f197defda
@ -658,13 +658,6 @@ void DiskAccessStorage::deleteAccessEntityOnDisk(const UUID & id) const
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::pair<UUID, AccessEntityPtr>> DiskAccessStorage::readAllForBackup(AccessEntityType type, const BackupSettings &) const
|
||||
{
|
||||
if (!isBackupAllowed())
|
||||
throwBackupNotAllowed();
|
||||
return readAllWithIDs(type);
|
||||
}
|
||||
|
||||
void DiskAccessStorage::insertFromBackup(
|
||||
const std::vector<std::pair<UUID, AccessEntityPtr>> & entities_from_backup,
|
||||
const RestoreSettings & restore_settings,
|
||||
|
@ -30,7 +30,6 @@ public:
|
||||
bool exists(const UUID & id) const override;
|
||||
|
||||
bool isBackupAllowed() const override { return backup_allowed; }
|
||||
std::vector<std::pair<UUID, AccessEntityPtr>> readAllForBackup(AccessEntityType type, const BackupSettings & backup_settings) const override;
|
||||
void insertFromBackup(const std::vector<std::pair<UUID, AccessEntityPtr>> & entities_from_backup, const RestoreSettings & restore_settings, std::shared_ptr<IRestoreCoordination> restore_coordination) override;
|
||||
|
||||
private:
|
||||
|
@ -52,6 +52,9 @@ struct IAccessEntity
|
||||
/// Replaces dependencies according to a specified map.
|
||||
virtual void replaceDependencies(const std::unordered_map<UUID, UUID> & /* old_to_new_ids */) {}
|
||||
|
||||
/// Whether this access entity should be written to a backup.
|
||||
virtual bool isBackupAllowed() const { return false; }
|
||||
|
||||
protected:
|
||||
String name;
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <base/FnTraits.h>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -524,11 +525,14 @@ bool IAccessStorage::isRestoreAllowed() const
|
||||
return isBackupAllowed() && !isReadOnly();
|
||||
}
|
||||
|
||||
std::vector<std::pair<UUID, AccessEntityPtr>> IAccessStorage::readAllForBackup(AccessEntityType, const BackupSettings &) const
|
||||
std::vector<std::pair<UUID, AccessEntityPtr>> IAccessStorage::readAllForBackup(AccessEntityType type, const BackupSettings &) const
|
||||
{
|
||||
if (!isBackupAllowed())
|
||||
throwBackupNotAllowed();
|
||||
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "readAllForBackup() is not implemented in {}", getStorageType());
|
||||
|
||||
auto res = readAllWithIDs(type);
|
||||
boost::range::remove_erase_if(res, [](const std::pair<UUID, AccessEntityPtr> & x) { return !x.second->isBackupAllowed(); });
|
||||
return res;
|
||||
}
|
||||
|
||||
void IAccessStorage::insertFromBackup(const std::vector<std::pair<UUID, AccessEntityPtr>> &, const RestoreSettings &, std::shared_ptr<IRestoreCoordination>)
|
||||
|
@ -272,14 +272,6 @@ void MemoryAccessStorage::setAll(const std::vector<std::pair<UUID, AccessEntityP
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::pair<UUID, AccessEntityPtr>> MemoryAccessStorage::readAllForBackup(AccessEntityType type, const BackupSettings &) const
|
||||
{
|
||||
if (!isBackupAllowed())
|
||||
throwBackupNotAllowed();
|
||||
|
||||
return readAllWithIDs(type);
|
||||
}
|
||||
|
||||
void MemoryAccessStorage::insertFromBackup(
|
||||
const std::vector<std::pair<UUID, AccessEntityPtr>> & entities_from_backup,
|
||||
const RestoreSettings & restore_settings,
|
||||
|
@ -28,7 +28,6 @@ public:
|
||||
bool exists(const UUID & id) const override;
|
||||
|
||||
bool isBackupAllowed() const override { return backup_allowed; }
|
||||
std::vector<std::pair<UUID, AccessEntityPtr>> readAllForBackup(AccessEntityType type, const BackupSettings & backup_settings) const override;
|
||||
void insertFromBackup(const std::vector<std::pair<UUID, AccessEntityPtr>> & entities_from_backup, const RestoreSettings & restore_settings, std::shared_ptr<IRestoreCoordination> restore_coordination) override;
|
||||
|
||||
private:
|
||||
|
@ -48,6 +48,7 @@ struct Quota : public IAccessEntity
|
||||
|
||||
std::vector<UUID> findDependencies() const override;
|
||||
void replaceDependencies(const std::unordered_map<UUID, UUID> & old_to_new_ids) override;
|
||||
bool isBackupAllowed() const override { return true; }
|
||||
};
|
||||
|
||||
using QuotaPtr = std::shared_ptr<const Quota>;
|
||||
|
@ -613,13 +613,6 @@ AccessEntityPtr ReplicatedAccessStorage::readImpl(const UUID & id, bool throw_if
|
||||
return entry.entity;
|
||||
}
|
||||
|
||||
std::vector<std::pair<UUID, AccessEntityPtr>> ReplicatedAccessStorage::readAllForBackup(AccessEntityType type, const BackupSettings &) const
|
||||
{
|
||||
if (!isBackupAllowed())
|
||||
throwBackupNotAllowed();
|
||||
return readAllWithIDs(type);
|
||||
}
|
||||
|
||||
void ReplicatedAccessStorage::insertFromBackup(const std::vector<std::pair<UUID, AccessEntityPtr>> & entities_from_backup, const RestoreSettings & restore_settings, std::shared_ptr<IRestoreCoordination> restore_coordination)
|
||||
{
|
||||
if (!isRestoreAllowed())
|
||||
|
@ -37,7 +37,6 @@ public:
|
||||
bool exists(const UUID & id) const override;
|
||||
|
||||
bool isBackupAllowed() const override { return backup_allowed; }
|
||||
std::vector<std::pair<UUID, AccessEntityPtr>> readAllForBackup(AccessEntityType type, const BackupSettings & backup_settings) const override;
|
||||
void insertFromBackup(const std::vector<std::pair<UUID, AccessEntityPtr>> & entities_from_backup, const RestoreSettings & restore_settings, std::shared_ptr<IRestoreCoordination> restore_coordination) override;
|
||||
|
||||
private:
|
||||
|
@ -22,6 +22,7 @@ struct Role : public IAccessEntity
|
||||
|
||||
std::vector<UUID> findDependencies() const override;
|
||||
void replaceDependencies(const std::unordered_map<UUID, UUID> & old_to_new_ids) override;
|
||||
bool isBackupAllowed() const override { return settings.isBackupAllowed(); }
|
||||
};
|
||||
|
||||
using RolePtr = std::shared_ptr<const Role>;
|
||||
|
@ -48,6 +48,7 @@ struct RowPolicy : public IAccessEntity
|
||||
|
||||
std::vector<UUID> findDependencies() const override;
|
||||
void replaceDependencies(const std::unordered_map<UUID, UUID> & old_to_new_ids) override;
|
||||
bool isBackupAllowed() const override { return true; }
|
||||
|
||||
/// Which roles or users should use this row policy.
|
||||
RolesOrUsersSet to_roles;
|
||||
|
@ -23,6 +23,7 @@ struct SettingsProfile : public IAccessEntity
|
||||
|
||||
std::vector<UUID> findDependencies() const override;
|
||||
void replaceDependencies(const std::unordered_map<UUID, UUID> & old_to_new_ids) override;
|
||||
bool isBackupAllowed() const override { return elements.isBackupAllowed(); }
|
||||
};
|
||||
|
||||
using SettingsProfilePtr = std::shared_ptr<const SettingsProfile>;
|
||||
|
@ -12,6 +12,13 @@
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const char ALLOW_BACKUP_SETTING_NAME[] = "allow_backup";
|
||||
}
|
||||
|
||||
|
||||
SettingsProfileElement::SettingsProfileElement(const ASTSettingsProfileElement & ast)
|
||||
{
|
||||
init(ast, nullptr);
|
||||
@ -41,7 +48,10 @@ void SettingsProfileElement::init(const ASTSettingsProfileElement & ast, const A
|
||||
|
||||
/// Optionally check if a setting with that name is allowed.
|
||||
if (access_control)
|
||||
access_control->checkSettingNameIsAllowed(setting_name);
|
||||
{
|
||||
if (setting_name != ALLOW_BACKUP_SETTING_NAME)
|
||||
access_control->checkSettingNameIsAllowed(setting_name);
|
||||
}
|
||||
|
||||
value = ast.value;
|
||||
min_value = ast.min_value;
|
||||
@ -168,8 +178,11 @@ Settings SettingsProfileElements::toSettings() const
|
||||
Settings res;
|
||||
for (const auto & elem : *this)
|
||||
{
|
||||
if (!elem.setting_name.empty() && !elem.value.isNull())
|
||||
res.set(elem.setting_name, elem.value);
|
||||
if (!elem.setting_name.empty() && (elem.setting_name != ALLOW_BACKUP_SETTING_NAME))
|
||||
{
|
||||
if (!elem.value.isNull())
|
||||
res.set(elem.setting_name, elem.value);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -179,8 +192,11 @@ SettingsChanges SettingsProfileElements::toSettingsChanges() const
|
||||
SettingsChanges res;
|
||||
for (const auto & elem : *this)
|
||||
{
|
||||
if (!elem.setting_name.empty() && !elem.value.isNull())
|
||||
res.push_back({elem.setting_name, elem.value});
|
||||
if (!elem.setting_name.empty() && (elem.setting_name != ALLOW_BACKUP_SETTING_NAME))
|
||||
{
|
||||
if (!elem.value.isNull())
|
||||
res.push_back({elem.setting_name, elem.value});
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -190,7 +206,7 @@ SettingsConstraints SettingsProfileElements::toSettingsConstraints(const AccessC
|
||||
SettingsConstraints res{access_control};
|
||||
for (const auto & elem : *this)
|
||||
{
|
||||
if (!elem.setting_name.empty())
|
||||
if (!elem.setting_name.empty() && (elem.setting_name != ALLOW_BACKUP_SETTING_NAME))
|
||||
{
|
||||
if (!elem.min_value.isNull())
|
||||
res.setMinValue(elem.setting_name, elem.min_value);
|
||||
@ -219,5 +235,14 @@ std::vector<UUID> SettingsProfileElements::toProfileIDs() const
|
||||
return res;
|
||||
}
|
||||
|
||||
bool SettingsProfileElements::isBackupAllowed() const
|
||||
{
|
||||
for (const auto & setting : *this)
|
||||
{
|
||||
if (setting.setting_name == ALLOW_BACKUP_SETTING_NAME)
|
||||
return static_cast<bool>(SettingFieldBool{setting.value});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -67,6 +67,8 @@ public:
|
||||
SettingsChanges toSettingsChanges() const;
|
||||
SettingsConstraints toSettingsConstraints(const AccessControl & access_control) const;
|
||||
std::vector<UUID> toProfileIDs() const;
|
||||
|
||||
bool isBackupAllowed() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ struct User : public IAccessEntity
|
||||
|
||||
std::vector<UUID> findDependencies() const override;
|
||||
void replaceDependencies(const std::unordered_map<UUID, UUID> & old_to_new_ids) override;
|
||||
bool isBackupAllowed() const override { return settings.isBackupAllowed(); }
|
||||
};
|
||||
|
||||
using UserPtr = std::shared_ptr<const User>;
|
||||
|
@ -453,7 +453,7 @@ def test_temporary_table():
|
||||
) == TSV([["e"], ["q"], ["w"]])
|
||||
|
||||
|
||||
# "BACKUP DATABASE _temporary_and_external_tables" is allowed but the backup must not contain any tables.
|
||||
# "BACKUP DATABASE _temporary_and_external_tables" is allowed but the backup must not contain these tables.
|
||||
def test_temporary_tables_database():
|
||||
session_id = new_session_id()
|
||||
instance.http_query(
|
||||
@ -607,8 +607,10 @@ def test_system_users_required_privileges():
|
||||
instance.query("CREATE USER u1 DEFAULT ROLE r1")
|
||||
instance.query("GRANT SELECT ON test.* TO u1")
|
||||
|
||||
# SETTINGS allow_backup=false means the following user won't be included in backups.
|
||||
instance.query("CREATE USER u2 SETTINGS allow_backup=false")
|
||||
|
||||
backup_name = new_backup_name()
|
||||
instance.query("CREATE USER u2")
|
||||
|
||||
expected_error = "necessary to have grant BACKUP ON system.users"
|
||||
assert expected_error in instance.query_and_get_error(
|
||||
@ -630,18 +632,27 @@ def test_system_users_required_privileges():
|
||||
instance.query("DROP USER u1")
|
||||
instance.query("DROP ROLE r1")
|
||||
|
||||
expected_error = "necessary to have grant CREATE USER ON *.*"
|
||||
assert expected_error in instance.query_and_get_error(f"RESTORE ALL FROM {backup_name}", user="u2")
|
||||
expected_error = (
|
||||
"necessary to have grant CREATE USER, CREATE ROLE, ROLE ADMIN ON *.*"
|
||||
)
|
||||
assert expected_error in instance.query_and_get_error(
|
||||
f"RESTORE ALL FROM {backup_name}", user="u2"
|
||||
)
|
||||
|
||||
instance.query("GRANT CREATE USER ON *.* TO u2")
|
||||
instance.query("GRANT CREATE USER, CREATE ROLE, ROLE ADMIN ON *.* TO u2")
|
||||
|
||||
expected_error = "necessary to have grant SELECT ON test.* WITH GRANT OPTION"
|
||||
assert expected_error in instance.query_and_get_error(
|
||||
f"RESTORE ALL FROM {backup_name}", user="u2"
|
||||
)
|
||||
|
||||
instance.query("GRANT SELECT ON test.* TO u2 WITH GRANT OPTION")
|
||||
instance.query(f"RESTORE ALL FROM {backup_name}", user="u2")
|
||||
|
||||
assert (
|
||||
instance.query("SHOW CREATE USER u1")
|
||||
== "CREATE USER u1 IDENTIFIED WITH sha256_password SETTINGS PROFILE default, custom_a = 1\n"
|
||||
)
|
||||
assert instance.query("SHOW CREATE USER u1") == "CREATE USER u1 DEFAULT ROLE r1\n"
|
||||
assert instance.query("SHOW GRANTS FOR u1") == TSV(
|
||||
["GRANT SELECT ON test.* TO u1", "GRANT r2 TO u1"]
|
||||
["GRANT SELECT ON test.* TO u1", "GRANT r1 TO u1"]
|
||||
)
|
||||
|
||||
assert instance.query("SHOW CREATE ROLE r1") == "CREATE ROLE r1\n"
|
||||
assert instance.query("SHOW GRANTS FOR r1") == ""
|
||||
|
Loading…
Reference in New Issue
Block a user