mirror of
https://github.com/borgbackup/borg.git
synced 2026-06-11 09:59:19 -04:00
Merge pull request #8844 from ThomasWaldmann/write-only
repository: add write-only permissions mode, fixes #1165
This commit is contained in:
commit
7afb649262
5 changed files with 82 additions and 3 deletions
|
|
@ -498,6 +498,8 @@ class Archive:
|
|||
deleted=False,
|
||||
):
|
||||
name_is_id = isinstance(name, bytes)
|
||||
if not name_is_id:
|
||||
assert len(name) <= 255
|
||||
self.cwd = os.getcwd()
|
||||
assert isinstance(manifest, Manifest)
|
||||
self.manifest = manifest
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from collections import namedtuple
|
|||
from datetime import datetime, timezone, timedelta
|
||||
from time import perf_counter
|
||||
|
||||
from borgstore.backends.errors import PermissionDenied
|
||||
|
||||
from .logger import create_logger
|
||||
|
||||
logger = create_logger()
|
||||
|
|
@ -443,7 +445,10 @@ class FilesCacheMixin:
|
|||
from .archive import Archive
|
||||
|
||||
# get the latest archive with the IDENTICAL name, supporting archive series:
|
||||
archives = self.manifest.archives.list(match=[self.archive_name], sort_by=["ts"], last=1)
|
||||
try:
|
||||
archives = self.manifest.archives.list(match=[self.archive_name], sort_by=["ts"], last=1)
|
||||
except PermissionDenied: # maybe repo is in write-only mode?
|
||||
archives = None
|
||||
if not archives:
|
||||
# nothing found
|
||||
return
|
||||
|
|
|
|||
|
|
@ -553,7 +553,6 @@ class Manifest:
|
|||
self.timestamp = max_ts.isoformat(timespec="microseconds")
|
||||
# include checks for limits as enforced by limited unpacker (used by load())
|
||||
assert self.archives.count() <= MAX_ARCHIVES
|
||||
assert all(len(name) <= 255 for name in self.archives.names())
|
||||
assert len(self.item_keys) <= 100
|
||||
self.config["item_keys"] = tuple(sorted(self.item_keys))
|
||||
manifest_archives = self.archives.finish(self)
|
||||
|
|
|
|||
|
|
@ -130,11 +130,22 @@ class Repository:
|
|||
"keys": "lr",
|
||||
"locks": "lrwD", # borg needs to create/delete a shared lock here
|
||||
}
|
||||
elif permissions == "write-only": # mostly no reading
|
||||
permissions = {
|
||||
"": "l",
|
||||
"archives": "lw",
|
||||
"cache": "lrwWD", # read allowed, e.g. for chunks.<HASH> cache
|
||||
"config": "lrW", # W for manifest
|
||||
"data": "lw", # no r!
|
||||
"keys": "lr",
|
||||
"locks": "lrwD", # borg needs to create/delete a shared lock here
|
||||
}
|
||||
elif permissions == "read-only": # mostly r/o
|
||||
permissions = {"": "lr", "locks": "lrwD"}
|
||||
else:
|
||||
raise Error(
|
||||
f"Invalid BORG_REPO_PERMISSIONS value: {permissions}, should be one of: all, no-delete, read-only"
|
||||
f"Invalid BORG_REPO_PERMISSIONS value: {permissions}, should be one of: "
|
||||
f"all, no-delete, write-only, read-only."
|
||||
)
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -136,3 +136,65 @@ def test_repository_permissions_read_only(archivers, request, monkeypatch):
|
|||
# Try to compact the repo, which should fail.
|
||||
with pytest.raises(PermissionDenied):
|
||||
cmd(archiver, "compact")
|
||||
|
||||
|
||||
def test_repository_permissions_write_only(archivers, request, monkeypatch):
|
||||
"""Test repository with 'write-only' permissions setting"""
|
||||
archiver = request.getfixturevalue(archivers)
|
||||
|
||||
# Create a repository first (need unrestricted permissions for that).
|
||||
monkeypatch.setenv("BORG_REPO_PERMISSIONS", "all")
|
||||
cmd(archiver, "repo-create", RK_ENCRYPTION)
|
||||
|
||||
# Create an initial archive to test with.
|
||||
create_test_files(archiver.input_path)
|
||||
cmd(archiver, "create", "archive1", "input")
|
||||
|
||||
# Switch to write-only permissions.
|
||||
monkeypatch.setenv("BORG_REPO_PERMISSIONS", "write-only")
|
||||
|
||||
# Try to create a new archive, which should succeed
|
||||
cmd(archiver, "create", "archive2", "input")
|
||||
|
||||
# Try to list archives, which should fail (requires reading from data directory).
|
||||
with pytest.raises(PermissionDenied):
|
||||
cmd(archiver, "repo-list")
|
||||
|
||||
# Try to list files in an archive, which should fail (requires reading from data directory).
|
||||
with pytest.raises(PermissionDenied):
|
||||
cmd(archiver, "list", "archive1")
|
||||
with pytest.raises(PermissionDenied):
|
||||
cmd(archiver, "list", "archive2")
|
||||
|
||||
# Try to extract the archive, which should fail (data dir has "lw" permissions, no reading).
|
||||
with pytest.raises(PermissionDenied):
|
||||
with changedir("output"):
|
||||
cmd(archiver, "extract", "archive1")
|
||||
|
||||
# Try to delete an archive, which should fail (requires reading from data directory to identify the archive).
|
||||
with pytest.raises(PermissionDenied):
|
||||
cmd(archiver, "delete", "archive1")
|
||||
|
||||
# Try to compact the repo, which should fail (data dir has "lw" permissions, no reading).
|
||||
with pytest.raises(PermissionDenied):
|
||||
cmd(archiver, "compact")
|
||||
|
||||
# Try to check the repo, which should fail (data dir has "lw" permissions, no reading).
|
||||
with pytest.raises(PermissionDenied):
|
||||
cmd(archiver, "check")
|
||||
|
||||
# Try to delete the repo, which should fail (no "D" permission on data dir).
|
||||
with pytest.raises(PermissionDenied):
|
||||
cmd(archiver, "repo-delete")
|
||||
|
||||
# Switch to read-only permissions.
|
||||
monkeypatch.setenv("BORG_REPO_PERMISSIONS", "read-only")
|
||||
|
||||
# Try to list archives, should work now.
|
||||
output = cmd(archiver, "repo-list")
|
||||
assert "archive1" in output
|
||||
assert "archive2" in output
|
||||
|
||||
# Try to list files in an archive, should work now.
|
||||
cmd(archiver, "list", "archive1")
|
||||
cmd(archiver, "list", "archive2")
|
||||
|
|
|
|||
Loading…
Reference in a new issue