Add support for setting 'allow_backup' to skip access entities from putting to backup.

This commit is contained in:
Vitaly Baranov 2022-06-19 12:49:50 +02:00
parent 01aaaf7395
commit 9f197defda
16 changed files with 68 additions and 43 deletions

View File

@ -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,

View File

@ -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:

View File

@ -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;

View File

@ -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>)

View File

@ -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,

View File

@ -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:

View File

@ -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>;

View File

@ -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())

View File

@ -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:

View File

@ -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>;

View File

@ -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;

View File

@ -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>;

View File

@ -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;
}
}

View File

@ -67,6 +67,8 @@ public:
SettingsChanges toSettingsChanges() const;
SettingsConstraints toSettingsConstraints(const AccessControl & access_control) const;
std::vector<UUID> toProfileIDs() const;
bool isBackupAllowed() const;
};
}

View File

@ -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>;

View File

@ -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") == ""