mirror of
https://github.com/9001/copyparty.git
synced 2026-08-14 14:23:08 -06:00
shadowing: //NULL as unmap keyword
This commit is contained in:
parent
12878e6c1c
commit
8884606138
14
README.md
14
README.md
|
|
@ -616,12 +616,26 @@ hiding specific subfolders by mounting another volume on top of them
|
|||
|
||||
for example `-v /mnt::r -v /var/empty:web/certs:` (note: no permissions) mounts the server folder `/mnt` as the webroot, but another volume is mounted at `/web/certs` -- so visitors can only see the contents of `/mnt` and `/mnt/web` (at URLs `/` and `/web`), but not `/mnt/web/certs` because URL `/web/certs` is mapped to `/var/empty`
|
||||
|
||||
to fully unmap it from the filesystem, specify `//NULL` instead of a real path such as `/var/empty`, so for example `-v /mnt::r -v //NULL:web/certs:` ensures `/web/certs` will never be accessible by anyone
|
||||
|
||||
the example config file right above this section may explain this better; the first volume `/` is mapped to `/srv` which means http://127.0.0.1:3923/music would try to read `/srv/music` on the server filesystem, but since there's another volume at `/music` mapped to `/mnt/music` then it'll go to `/mnt/music` instead
|
||||
|
||||
so, to shadow a file/folder, define a volume but leave out the `accs:` section
|
||||
|
||||
> ℹ️ this also works for single files, because files can also be volumes
|
||||
|
||||
config file example for unmapping folders by shadowing:
|
||||
|
||||
```yaml
|
||||
[/drives]
|
||||
/mnt # url "/drives" goes to "/mnt"
|
||||
accs:
|
||||
r: * # everyone can read
|
||||
|
||||
[/drives/foo/bar]
|
||||
//NULL # blocks access to "/mnt/foo/bar"
|
||||
```
|
||||
|
||||
|
||||
## dotfiles
|
||||
|
||||
|
|
|
|||
|
|
@ -1195,7 +1195,7 @@ class AuthSrv(object):
|
|||
for ptn, sigil in ((PTN_U_ANY, "${u}"), (PTN_G_ANY, "${g}")):
|
||||
if bool(ptn.search(src)) != bool(ptn.search(dst)):
|
||||
zsl.append(sigil)
|
||||
if zsl:
|
||||
if zsl and src != "//NULL":
|
||||
t = "ERROR: if %s is mentioned in a volume definition, it must be included in both the filesystem-path [%s] and the volume-url [/%s]"
|
||||
t = "\n".join([t % (x, src, dst) for x in zsl])
|
||||
self.log(t, 1)
|
||||
|
|
@ -1275,8 +1275,16 @@ class AuthSrv(object):
|
|||
daxs: dict[str, AXS],
|
||||
mflags: dict[str, dict[str, Any]],
|
||||
) -> tuple[str, str]:
|
||||
src = os.path.expanduser(self.args.shenvexp(src))
|
||||
src = absreal(src)
|
||||
if src == "//NULL":
|
||||
src = ""
|
||||
else:
|
||||
src = os.path.expanduser(self.args.shenvexp(src))
|
||||
src = absreal(src)
|
||||
|
||||
if dst == "//NULL":
|
||||
t = "//NULL given as URL instead of filesystem-path"
|
||||
self.log(t, 1)
|
||||
raise Exception(t)
|
||||
dst = dst.strip("/")
|
||||
|
||||
if dst in mount:
|
||||
|
|
@ -1284,7 +1292,7 @@ class AuthSrv(object):
|
|||
self.log(t.format(dst, mount[dst][0], src), c=1)
|
||||
raise Exception(BAD_CFG)
|
||||
|
||||
if src in mount.values():
|
||||
if src and src in mount.values():
|
||||
t = "filesystem-path [{}] mounted in multiple locations:"
|
||||
t = t.format(src)
|
||||
for v in [k for k, v in mount.items() if v[0] == src] + [dst]:
|
||||
|
|
@ -1293,7 +1301,7 @@ class AuthSrv(object):
|
|||
self.log(t, c=3)
|
||||
raise Exception(BAD_CFG)
|
||||
|
||||
if not bos.path.exists(src):
|
||||
if src and not bos.path.exists(src):
|
||||
self.log("warning: filesystem-path did not exist: %r" % (src,), 3)
|
||||
|
||||
vf = {}
|
||||
|
|
@ -1861,7 +1869,7 @@ class AuthSrv(object):
|
|||
if WINDOWS:
|
||||
cased = {}
|
||||
for vp, (ap, vp0) in mount.items():
|
||||
cased[vp] = (absreal(ap), vp0)
|
||||
cased[vp] = (absreal(ap) if ap else "", vp0)
|
||||
|
||||
mount = cased
|
||||
|
||||
|
|
|
|||
|
|
@ -15,3 +15,9 @@
|
|||
|
||||
[/vb]
|
||||
/b
|
||||
|
||||
[/x1]
|
||||
//NULL
|
||||
|
||||
[/x2]
|
||||
//NULL
|
||||
|
|
|
|||
|
|
@ -27,3 +27,9 @@
|
|||
/c
|
||||
accs:
|
||||
r: @ga, uc
|
||||
|
||||
[/x1]
|
||||
//NULL
|
||||
|
||||
[/x2]
|
||||
//NULL
|
||||
|
|
|
|||
|
|
@ -89,12 +89,17 @@ class TestVFS(unittest.TestCase):
|
|||
|
||||
self.assertEqual(au.vfs.vpath, "")
|
||||
self.assertApEq(au.vfs.realpath, "/")
|
||||
self.assertNodes(au.vfs, ["vb"])
|
||||
self.assertNodes(au.vfs, ["vb", "x1", "x2"])
|
||||
self.assertNodes(au.vfs.nodes["vb"], [])
|
||||
self.assertJump(au.vfs.nodes["x1"], [])
|
||||
self.assertJump(au.vfs.nodes["x2"], [])
|
||||
|
||||
self.assertAxs(au.vfs.axs, [["ua"]])
|
||||
self.assertAxs(au.vfs.nodes["vb"].axs, [])
|
||||
|
||||
self.assertEqual("", au.vfs.nodes["x1"].realpath)
|
||||
self.assertEqual("", au.vfs.nodes["x2"].realpath)
|
||||
|
||||
def test_2(self):
|
||||
"""
|
||||
users ua/ub/uc, group ga (ua+ub) in basic combinations
|
||||
|
|
@ -104,15 +109,20 @@ class TestVFS(unittest.TestCase):
|
|||
|
||||
self.assertEqual(au.vfs.vpath, "")
|
||||
self.assertApEq(au.vfs.realpath, "/")
|
||||
self.assertNodes(au.vfs, ["vb", "vc"])
|
||||
self.assertNodes(au.vfs, ["vb", "vc", "x1", "x2"])
|
||||
self.assertNodes(au.vfs.nodes["vb"], [])
|
||||
self.assertNodes(au.vfs.nodes["vc"], [])
|
||||
self.assertJump(au.vfs.nodes["x1"], [])
|
||||
self.assertJump(au.vfs.nodes["x2"], [])
|
||||
|
||||
self.assertAxs(au.vfs.axs, [["ua", "ub"]])
|
||||
self.assertAxsAt(au, "vb", [["ua", "ub"]]) # same as:
|
||||
self.assertAxs(au.vfs.nodes["vb"].axs, [["ua", "ub"]])
|
||||
self.assertAxs(au.vfs.nodes["vc"].axs, [["ua", "ub", "uc"]])
|
||||
|
||||
self.assertEqual("", au.vfs.nodes["x1"].realpath)
|
||||
self.assertEqual("", au.vfs.nodes["x2"].realpath)
|
||||
|
||||
def test_3(self):
|
||||
"""
|
||||
IdP-only; dynamically created volumes for users/groups
|
||||
|
|
|
|||
|
|
@ -248,6 +248,20 @@ class TestVFS(unittest.TestCase):
|
|||
self.assertEqual(r1, r2)
|
||||
self.assertEqual(list(v1), list(v2))
|
||||
|
||||
# shadowing II
|
||||
self.wipe_vfs(td)
|
||||
vfs = AuthSrv(Cfg(v=[".::r", "//NULL:a/ab:", "//NULL:a/ac:"]), self.log).vfs
|
||||
|
||||
fsp, r1, v1 = self.ls(vfs, "", "*")
|
||||
self.assertEqual(fsp, td)
|
||||
self.assertEqual(r1, ["b", "c"])
|
||||
self.assertEqual(list(v1), ["a"])
|
||||
|
||||
fsp, r1, v1 = self.ls(vfs, "a", "*")
|
||||
self.assertEqual(fsp, os.path.join(td, "a"))
|
||||
self.assertEqual(r1, ["aa"])
|
||||
self.assertEqual(list(v1), []) # ["ab", "ac"]
|
||||
|
||||
# config file parser
|
||||
cfg_path = os.path.join(self.td, "test.cfg")
|
||||
with open(cfg_path, "wb") as f:
|
||||
|
|
|
|||
Loading…
Reference in a new issue