list dotfiles only for specific volumes or users (#66):

* permission `.` grants dotfile visibility if user has `r` too
* `-ed` will grant dotfiles to all `r` accounts (same as before)
* volflag `dots` likewise

also drops compatibility for pre-0.12.0 `-v` syntax
(`-v .::red` will no longer translate to `-v .::r,ed`)
This commit is contained in:
ed
2023-12-16 15:38:48 +00:00
parent c057c5e8e8
commit 0c50ea1757
12 changed files with 306 additions and 167 deletions

111
tests/test_dots.py Normal file
View File

@@ -0,0 +1,111 @@
#!/usr/bin/env python3
# coding: utf-8
from __future__ import print_function, unicode_literals
import io
import os
import shutil
import tarfile
import tempfile
import unittest
from copyparty.authsrv import AuthSrv
from copyparty.httpcli import HttpCli
from copyparty.up2k import Up2k
from copyparty.u2idx import U2idx
from tests import util as tu
from tests.util import Cfg
def hdr(query, uname):
h = "GET /%s HTTP/1.1\r\nPW: %s\r\nConnection: close\r\n\r\n"
return (h % (query, uname)).encode("utf-8")
class TestHttpCli(unittest.TestCase):
def setUp(self):
self.td = tu.get_ramdisk()
def tearDown(self):
os.chdir(tempfile.gettempdir())
shutil.rmtree(self.td)
def test(self):
td = os.path.join(self.td, "vfs")
os.mkdir(td)
os.chdir(td)
# topDir volA volA/*dirA .volB .volB/*dirB
spaths = " t .t a a/da a/.da .b .b/db .b/.db"
for n, dirpath in enumerate(spaths.split(" ")):
if dirpath:
os.makedirs(dirpath)
for pfx in "f", ".f":
filepath = pfx + str(n)
if dirpath:
filepath = os.path.join(dirpath, filepath)
with open(filepath, "wb") as f:
f.write(filepath.encode("utf-8"))
vcfg = [
".::r,u1:r.,u2",
"a:a:r,u1:r,u2",
".b:.b:r.,u1:r,u2"
]
self.args = Cfg(v=vcfg, a=["u1:u1", "u2:u2"], e2dsa=True)
self.asrv = AuthSrv(self.args, self.log)
self.assertEqual(self.tardir("", "u1"), "f0 t/f1 a/f3 a/da/f4")
self.assertEqual(self.tardir(".t", "u1"), "f2")
self.assertEqual(self.tardir(".b", "u1"), ".f6 f6 .db/.f8 .db/f8 db/.f7 db/f7")
zs = ".f0 f0 .t/.f2 .t/f2 t/.f1 t/f1 .b/f6 .b/db/f7 a/f3 a/da/f4"
self.assertEqual(self.tardir("", "u2"), zs)
self.assertEqual(self.curl("?tar", "x")[1][:17], "\nJ2EOT")
# search
up2k = Up2k(self)
u2idx = U2idx(self)
allvols = list(self.asrv.vfs.all_vols.values())
x = u2idx.search("u1", allvols, "", 999)
x = " ".join(sorted([x["rp"] for x in x[0]]))
# u1 can see dotfiles in volB so they should be included
xe = ".b/.db/.f8 .b/.db/f8 .b/.f6 .b/db/.f7 .b/db/f7 .b/f6 a/da/f4 a/f3 f0 t/f1"
self.assertEqual(x, xe)
x = u2idx.search("u2", allvols, "", 999)
x = " ".join(sorted([x["rp"] for x in x[0]]))
self.assertEqual(x, ".f0 .t/.f2 .t/f2 a/da/f4 a/f3 f0 t/.f1 t/f1")
self.args = Cfg(v=vcfg, a=["u1:u1", "u2:u2"], dotsrch=False)
self.asrv = AuthSrv(self.args, self.log)
u2idx = U2idx(self)
x = u2idx.search("u1", self.asrv.vfs.all_vols.values(), "", 999)
x = " ".join(sorted([x["rp"] for x in x[0]]))
# u1 can see dotfiles in volB so they should be included
xe = "a/da/f4 a/f3 f0 t/f1"
self.assertEqual(x, xe)
def tardir(self, url, uname):
h, b = self.curl("/" + url + "?tar", uname, True)
tar = tarfile.open(fileobj=io.BytesIO(b), mode="r|").getnames()
top = ("top" if not url else url.lstrip(".").split("/")[0]) + "/"
assert len(tar) == len([x for x in tar if x.startswith(top)])
return " ".join([x[len(top):] for x in tar])
def curl(self, url, uname, binary=False):
conn = tu.VHttpConn(self.args, self.asrv, self.log, hdr(url, uname))
HttpCli(conn).run()
if binary:
h, b = conn.s._reply.split(b"\r\n\r\n", 1)
return [h.decode("utf-8"), b]
return conn.s._reply.decode("utf-8").split("\r\n\r\n", 1)
def log(self, src, msg, c=0):
print(msg)

View File

@@ -7,7 +7,6 @@ import os
import shutil
import tempfile
import unittest
from textwrap import dedent
from copyparty import util
from copyparty.authsrv import VFS, AuthSrv
@@ -175,11 +174,11 @@ class TestVFS(unittest.TestCase):
self.assertEqual(len(vfs.nodes), 1)
self.assertEqual(n.vpath, "a")
self.assertEqual(n.realpath, os.path.join(td, "a"))
self.assertAxs(n.axs.uread, ["*"])
self.assertAxs(n.axs.uread, ["*", "k"])
self.assertAxs(n.axs.uwrite, [])
perm_na = (False, False, False, False, False, False, False)
perm_rw = (True, True, False, False, False, False, False)
perm_ro = (True, False, False, False, False, False, False)
perm_na = (False, False, False, False, False, False, False, False)
perm_rw = (True, True, False, False, False, False, False, False)
perm_ro = (True, False, False, False, False, False, False, False)
self.assertEqual(vfs.can_access("/", "*"), perm_na)
self.assertEqual(vfs.can_access("/", "k"), perm_rw)
self.assertEqual(vfs.can_access("/a", "*"), perm_ro)
@@ -232,7 +231,7 @@ class TestVFS(unittest.TestCase):
cfg_path = os.path.join(self.td, "test.cfg")
with open(cfg_path, "wb") as f:
f.write(
dedent(
util.dedent(
"""
u a:123
u asd:fgh:jkl

View File

@@ -44,6 +44,7 @@ if MACOS:
from copyparty.__init__ import E
from copyparty.__main__ import init_E
from copyparty.util import FHC, Garda, Unrecv
from copyparty.u2idx import U2idx
init_E(E)
@@ -106,51 +107,59 @@ def get_ramdisk():
class Cfg(Namespace):
def __init__(self, a=None, v=None, c=None):
def __init__(self, a=None, v=None, c=None, **ka0):
ka = {}
ex = "daw dav_auth dav_inf dav_mac dav_rt dotsrch e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp ed emp exp force_js getmod grid hardlink ih ihead magic never_symlink nid nih no_acode no_athumb no_dav no_dedup no_del no_dupe no_logues no_mv no_readme no_robots no_sb_md no_sb_lg no_scandir no_tarcmp no_thumb no_vthumb no_zip nrand nw rand smb th_no_crop vague_403 vc ver xdev xlink xvol"
ex = "daw dav_auth dav_inf dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp ed emp exp force_js getmod grid hardlink ih ihead magic never_symlink nid nih no_acode no_athumb no_dav no_dedup no_del no_dupe no_lifetime no_logues no_mv no_readme no_robots no_sb_md no_sb_lg no_scandir no_tarcmp no_thumb no_vthumb no_zip nrand nw rand smb srch_dbg stats th_no_crop vague_403 vc ver xdev xlink xvol"
ka.update(**{k: False for k in ex.split()})
ex = "dotpart no_rescan no_sendfile no_voldump plain_ip"
ex = "dotpart dotsrch no_dhash no_fastboot no_rescan no_sendfile no_voldump re_dhash plain_ip"
ka.update(**{k: True for k in ex.split()})
ex = "ah_cli ah_gen css_browser hist ipa_re js_browser no_forget no_hash no_idx nonsus_urls"
ka.update(**{k: None for k in ex.split()})
ex = "s_thead s_tbody th_convt"
ex = "hash_mt srch_time"
ka.update(**{k: 1 for k in ex.split()})
ex = "reg_cap s_thead s_tbody th_convt"
ka.update(**{k: 9 for k in ex.split()})
ex = "df loris re_maxage rproxy rsp_jtr rsp_slp s_wr_slp theme themes turbo"
ex = "db_act df loris re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo"
ka.update(**{k: 0 for k in ex.split()})
ex = "ah_alg bname doctitle favico hdr_au_usr html_head lg_sbf log_fk md_sbf name textfiles unlist vname R RS SR"
ex = "ah_alg bname doctitle exit favico hdr_au_usr html_head lg_sbf log_fk md_sbf name textfiles unlist vname R RS SR"
ka.update(**{k: "" for k in ex.split()})
ex = "on403 on404 xad xar xau xban xbd xbr xbu xiu xm"
ka.update(**{k: [] for k in ex.split()})
ex = "exp_lg exp_md"
ex = "exp_lg exp_md th_coversd"
ka.update(**{k: {} for k in ex.split()})
ka.update(ka0)
super(Cfg, self).__init__(
a=a or [],
v=v or [],
c=c,
E=E,
dbd="wal",
s_wr_sz=512 * 1024,
th_size="320x256",
fk_salt="a" * 16,
unpost=600,
u2sort="s",
u2ts="c",
sort="href",
mtp=[],
lang="eng",
log_badpwd=1,
logout=573,
mte={"a": True},
mth={},
lang="eng",
logout=573,
mtp=[],
s_wr_sz=512 * 1024,
sort="href",
srch_hits=99999,
th_size="320x256",
u2sort="s",
u2ts="c",
unpost=600,
warksalt="hunter2",
**ka
)
@@ -186,11 +195,16 @@ class VSock(object):
class VHttpSrv(object):
def __init__(self):
def __init__(self, args, asrv, log):
self.args = args
self.asrv = asrv
self.log = log
self.broker = NullBroker()
self.prism = None
self.bans = {}
self.nreq = 0
self.nsus = 0
aliases = ["splash", "browser", "browser2", "msg", "md", "mde"]
self.j2 = {x: J2_FILES for x in aliases}
@@ -200,31 +214,38 @@ class VHttpSrv(object):
self.g403 = Garda("")
self.gurl = Garda("")
self.u2idx = None
self.ptn_cc = re.compile(r"[\x00-\x1f]")
def cachebuster(self):
return "a"
def get_u2idx(self):
self.u2idx = self.u2idx or U2idx(self)
return self.u2idx
class VHttpConn(object):
def __init__(self, args, asrv, log, buf):
self.t0 = time.time()
self.s = VSock(buf)
self.sr = Unrecv(self.s, None) # type: ignore
self.aclose = {}
self.addr = ("127.0.0.1", "42069")
self.args = args
self.asrv = asrv
self.nid = None
self.bans = {}
self.freshen_pwd = 0.0
self.hsrv = VHttpSrv(args, asrv, log)
self.ico = None
self.lf_url = None
self.log_func = log
self.log_src = "a"
self.lf_url = None
self.hsrv = VHttpSrv()
self.bans = {}
self.aclose = {}
self.u2fh = FHC()
self.mutex = threading.Lock()
self.nreq = -1
self.nbyte = 0
self.ico = None
self.nid = None
self.nreq = -1
self.thumbcli = None
self.freshen_pwd = 0.0
self.t0 = time.time()
self.u2fh = FHC()
self.get_u2idx = self.hsrv.get_u2idx