From d8bddede6a09438d219b51d03bf2c2c7c9394a72 Mon Sep 17 00:00:00 2001 From: ed Date: Sat, 8 Oct 2022 01:17:41 +0200 Subject: [PATCH] new permission G returns filekey on write-only uploads --- copyparty/__main__.py | 2 +- copyparty/authsrv.py | 30 ++++++++++++++++++++---------- copyparty/ftpd.py | 13 +++++++++++-- copyparty/httpcli.py | 21 +++++++++++++++------ copyparty/svchub.py | 2 +- 5 files changed, 48 insertions(+), 20 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 2a626616..25efac23 100755 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -879,7 +879,7 @@ def main(argv: Optional[list[str]] = None) -> None: if re.match("c[^,]", opt): mod = True na.append("c," + opt[1:]) - elif re.sub("^[rwmdg]*", "", opt) and "," not in opt: + elif re.sub("^[rwmdgG]*", "", opt) and "," not in opt: mod = True perm = opt[0] if perm == "a": diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index 5a31b38c..136683bd 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -58,18 +58,20 @@ class AXS(object): umove: Optional[Union[list[str], set[str]]] = None, udel: Optional[Union[list[str], set[str]]] = None, uget: Optional[Union[list[str], set[str]]] = None, + upget: Optional[Union[list[str], set[str]]] = None, ) -> None: self.uread: set[str] = set(uread or []) self.uwrite: set[str] = set(uwrite or []) self.umove: set[str] = set(umove or []) self.udel: set[str] = set(udel or []) self.uget: set[str] = set(uget or []) + self.upget: set[str] = set(upget or []) def __repr__(self) -> str: return "AXS({})".format( ", ".join( "{}={!r}".format(k, self.__dict__[k]) - for k in "uread uwrite umove udel uget".split() + for k in "uread uwrite umove udel uget upget".split() ) ) @@ -293,6 +295,7 @@ class VFS(object): self.amove: dict[str, list[str]] = {} self.adel: dict[str, list[str]] = {} self.aget: dict[str, list[str]] = {} + self.apget: dict[str, list[str]] = {} if realpath: self.histpath = os.path.join(realpath, ".hist") # db / thumbcache @@ -384,8 +387,10 @@ class VFS(object): return self, vpath - def can_access(self, vpath: str, uname: str) -> tuple[bool, bool, bool, bool, bool]: - """can Read,Write,Move,Delete,Get""" + def can_access( + self, vpath: str, uname: str + ) -> tuple[bool, bool, bool, bool, bool, bool]: + """can Read,Write,Move,Delete,Get,Upget""" vn, _ = self._find(vpath) c = vn.axs return ( @@ -394,6 +399,7 @@ class VFS(object): uname in c.umove or "*" in c.umove, uname in c.udel or "*" in c.udel, uname in c.uget or "*" in c.uget, + uname in c.upget or "*" in c.upget, ) def get( @@ -728,7 +734,7 @@ class AuthSrv(object): def _read_vol_str( self, lvl: str, uname: str, axs: AXS, flags: dict[str, Any] ) -> None: - if lvl.strip("crwmdg"): + if lvl.strip("crwmdgG"): raise Exception("invalid volflag: {},{}".format(lvl, uname)) if lvl == "c": @@ -758,7 +764,9 @@ class AuthSrv(object): ("m", axs.umove), ("d", axs.udel), ("g", axs.uget), - ]: + ("G", axs.uget), + ("G", axs.upget), + ]: # b bb bbb if ch in lvl: al.add(un) @@ -808,7 +816,7 @@ class AuthSrv(object): if self.args.v: # list of src:dst:permset:permset:... - # permset is [,username][,username] or ,[=args] + # permset is [,username][,username] or ,[=args] for v_str in self.args.v: m = re_vol.match(v_str) if not m: @@ -873,7 +881,7 @@ class AuthSrv(object): vfs.all_vols = {} vfs.get_all_vols(vfs.all_vols) - for perm in "read write move del get".split(): + for perm in "read write move del get pget".split(): axs_key = "u" + perm unames = ["*"] + list(acct.keys()) umap: dict[str, list[str]] = {x: [] for x in unames} @@ -888,7 +896,7 @@ class AuthSrv(object): all_users = {} missing_users = {} for axs in daxs.values(): - for d in [axs.uread, axs.uwrite, axs.umove, axs.udel, axs.uget]: + for d in [axs.uread, axs.uwrite, axs.umove, axs.udel, axs.uget, axs.upget]: for usr in d: all_users[usr] = 1 if usr != "*" and usr not in acct: @@ -1193,6 +1201,7 @@ class AuthSrv(object): [" move", "umove"], ["delete", "udel"], [" get", "uget"], + [" upget", "upget"], ]: u = list(sorted(getattr(zv.axs, attr))) u = ", ".join("\033[35meverybody\033[0m" if x == "*" else x for x in u) @@ -1288,10 +1297,11 @@ class AuthSrv(object): raise Exception("volume not found: " + zs) self.log(str({"users": users, "vols": vols, "flags": flags})) - t = "/{}: read({}) write({}) move({}) del({}) get({})" + t = "/{}: read({}) write({}) move({}) del({}) get({}) upget({})" for k, zv in self.vfs.all_vols.items(): vc = zv.axs - self.log(t.format(k, vc.uread, vc.uwrite, vc.umove, vc.udel, vc.uget)) + vs = [k, vc.uread, vc.uwrite, vc.umove, vc.udel, vc.uget, vc.upget] + self.log(t.format(*vs)) flag_v = "v" in flags flag_ln = "ln" in flags diff --git a/copyparty/ftpd.py b/copyparty/ftpd.py index b0d291cf..3b41a270 100644 --- a/copyparty/ftpd.py +++ b/copyparty/ftpd.py @@ -94,6 +94,9 @@ class FtpFs(AbstractedFS): self.cwd = "/" # pyftpdlib convention of leading slash self.root = "/var/lib/empty" + self.can_read = self.can_write = self.can_move = False + self.can_delete = self.can_get = self.can_upget = False + self.listdirinfo = self.listdir self.chdir(".") @@ -153,8 +156,14 @@ class FtpFs(AbstractedFS): def chdir(self, path: str) -> None: self.cwd = join(self.cwd, path) - x = self.hub.asrv.vfs.can_access(self.cwd.lstrip("/"), self.h.username) - self.can_read, self.can_write, self.can_move, self.can_delete, self.can_get = x + ( + self.can_read, + self.can_write, + self.can_move, + self.can_delete, + self.can_get, + self.can_upget, + ) = self.hub.asrv.vfs.can_access(self.cwd.lstrip("/"), self.h.username) def mkdir(self, path: str) -> None: ap = self.rv2a(path, w=True) diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 7cf2b9a5..ddeb033f 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -147,6 +147,7 @@ class HttpCli(object): self.can_move = False self.can_delete = False self.can_get = False + self.can_upget = False # post self.parser: Optional[MultipartParser] = None # end placeholders @@ -363,6 +364,7 @@ class HttpCli(object): self.mvol = self.asrv.vfs.amove[self.uname] self.dvol = self.asrv.vfs.adel[self.uname] self.gvol = self.asrv.vfs.aget[self.uname] + self.upvol = self.asrv.vfs.apget[self.uname] if pwd: self.out_headerlist.append(("Set-Cookie", self.get_pwd_cookie(pwd)[0])) @@ -376,8 +378,14 @@ class HttpCli(object): ptn: Optional[Pattern[str]] = self.conn.lf_url # mypy404 self.do_log = not ptn or not ptn.search(self.req) - x = self.asrv.vfs.can_access(self.vpath, self.uname) - self.can_read, self.can_write, self.can_move, self.can_delete, self.can_get = x + ( + self.can_read, + self.can_write, + self.can_move, + self.can_delete, + self.can_get, + self.can_upget, + ) = self.asrv.vfs.can_access(self.vpath, self.uname) try: if self.mode in ["GET", "HEAD"]: @@ -885,7 +893,7 @@ class HttpCli(object): ) vsuf = "" - if self.can_read and "fk" in vfs.flags: + if (self.can_read or self.can_upget) and "fk" in vfs.flags: vsuf = "?k=" + self.gen_fk( self.args.fk_salt, path, @@ -1544,7 +1552,7 @@ class HttpCli(object): for sz, sha_hex, sha_b64, ofn, lfn, ap in files: vsuf = "" - if self.can_read and "fk" in vfs.flags: + if (self.can_read or self.can_upget) and "fk" in vfs.flags: vsuf = "?k=" + self.gen_fk( self.args.fk_salt, ap, @@ -2494,9 +2502,8 @@ class HttpCli(object): if not is_dir and (self.can_read or self.can_get): if not self.can_read and "fk" in vn.flags: - vabs = vjoin(vn.realpath, rem) correct = self.gen_fk( - self.args.fk_salt, vabs, st.st_size, 0 if ANYWIN else st.st_ino + self.args.fk_salt, abspath, st.st_size, 0 if ANYWIN else st.st_ino )[: vn.flags["fk"]] got = self.uparam.get("k") if got != correct: @@ -2541,6 +2548,8 @@ class HttpCli(object): perms.append("delete") if self.can_get: perms.append("get") + if self.can_upget: + perms.append("upget") url_suf = self.urlq({}, ["k"]) is_ls = "ls" in self.uparam diff --git a/copyparty/svchub.py b/copyparty/svchub.py index f043c44f..2b24ddcb 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -422,7 +422,7 @@ class SvcHub(object): with self.log_mutex: ts = datetime.utcnow().strftime("%Y-%m%d-%H%M%S.%f")[:-3] - self.logf.write("@{} [{}] {}\n".format(ts, src, msg)) + self.logf.write("@{} [{}\033[0m] {}\n".format(ts, src, msg)) now = time.time() if now >= self.next_day: