diff --git a/copyparty/__main__.py b/copyparty/__main__.py index faf86526..f6a4a60e 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1263,7 +1263,8 @@ def add_optouts(ap): ap2.add_argument("-nih", action="store_true", help="no info hostname -- don't show in UI") ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI") ap2.add_argument("-nb", action="store_true", help="no powered-by-copyparty branding in UI") - ap2.add_argument("--no-zip", action="store_true", help="disable download as zip/tar") + ap2.add_argument("--zip-who", metavar="LVL", type=int, default=3, help="who can download as zip/tar? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=authenticated-with-read-access, [\033[32m3\033[0m]=everyone-with-read-access (volflag=zip_who)\n\033[1;31mWARNING:\033[0m if a nested volume has a more restrictive value than a parent volume, then this will be \033[33mignored\033[0m if the download is initiated from the parent, more lenient volume") + ap2.add_argument("--no-zip", action="store_true", help="disable download as zip/tar; same as \033[33m--zip-who=0\033[0m") ap2.add_argument("--no-tarcmp", action="store_true", help="disable download as compressed tar (?tar=gz, ?tar=bz2, ?tar=xz, ?tar=gz:9, ...)") ap2.add_argument("--no-lifetime", action="store_true", help="do not allow clients (or server config) to schedule an upload to be deleted after a given time") ap2.add_argument("--no-pipe", action="store_true", help="disable race-the-beam (lockstep download of files which are currently being uploaded) (volflag=nopipe)") diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index 58c228be..7313c39b 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -1914,7 +1914,7 @@ class AuthSrv(object): if k not in vol.flags: vol.flags[k] = getattr(self.args, k) - for k in ("nrand", "u2abort"): + for k in ("nrand", "u2abort", "ups_who", "zip_who"): if k in vol.flags: vol.flags[k] = int(vol.flags[k]) diff --git a/copyparty/cfg.py b/copyparty/cfg.py index 09c8a5fb..323d0c98 100644 --- a/copyparty/cfg.py +++ b/copyparty/cfg.py @@ -94,6 +94,7 @@ def vf_vmap() -> dict[str, str]: "u2abort", "u2ts", "ups_who", + "zip_who", ): ret[k] = k return ret @@ -252,6 +253,8 @@ flagcats = { "dots": "allow all users with read-access to\nenable the option to show dotfiles in listings", "fk=8": 'generates per-file accesskeys,\nwhich are then required at the "g" permission;\nkeys are invalidated if filesize or inode changes', "fka=8": 'generates slightly weaker per-file accesskeys,\nwhich are then required at the "g" permission;\nnot affected by filesize or inode numbers', + "ups_who=2": "restrict viewing the list of recent uploads", + "zip_who=2": "restrict access to download-as-zip/tar", "mv_retry": "ms-windows: timeout for renaming busy files", "rm_retry": "ms-windows: timeout for deleting busy files", "davauth": "ask webdav clients to login for all folders", diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 697521f8..2c531ac2 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -4283,8 +4283,14 @@ class HttpCli(object): rem: str, items: list[str], ) -> bool: - if self.args.no_zip: - raise Pebkac(400, "not enabled in server config") + lvl = vn.flags["zip_who"] + if self.args.no_zip or not lvl: + raise Pebkac(400, "download-as-zip/tar is disabled in server config") + elif lvl <= 1 and not self.can_admin: + raise Pebkac(400, "download-as-zip/tar is admin-only on this server") + elif lvl <= 2 and self.uname in ("", "*"): + t = "you must be authenticated to download-as-zip/tar on this server" + raise Pebkac(400, t) logmsg = "{:4} {} ".format("", self.req) self.keepalive = False @@ -5152,7 +5158,7 @@ class HttpCli(object): adm = "*" in vol.axs.uadmin or self.uname in vol.axs.uadmin dots = "*" in vol.axs.udot or self.uname in vol.axs.udot - lvl = int(vol.flags["ups_who"]) + lvl = vol.flags["ups_who"] if not lvl: continue elif lvl == 1 and not adm: diff --git a/tests/util.py b/tests/util.py index 1cf01f00..fe171666 100644 --- a/tests/util.py +++ b/tests/util.py @@ -141,7 +141,7 @@ class Cfg(Namespace): ex = "hash_mt hsortn safe_dedup srch_time u2abort u2j u2sz" ka.update(**{k: 1 for k in ex.split()}) - ex = "au_vol dl_list mtab_age reg_cap s_thead s_tbody th_convt ups_who" + ex = "au_vol dl_list mtab_age reg_cap s_thead s_tbody th_convt ups_who zip_who" ka.update(**{k: 9 for k in ex.split()}) ex = "db_act k304 loris no304 re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo"