From edafa1586ae392b33d872104c9498b157c966c47 Mon Sep 17 00:00:00 2001 From: ed Date: Sun, 7 Sep 2025 17:20:51 +0000 Subject: [PATCH] volflag to block sharing of a volume --- copyparty/__main__.py | 1 + copyparty/authsrv.py | 17 +++++++++++++++-- copyparty/cfg.py | 2 ++ copyparty/httpcli.py | 8 ++++++++ copyparty/web/browser.js | 13 ++++++++++--- copyparty/web/up2k.js | 1 + tests/util.py | 1 + 7 files changed, 38 insertions(+), 5 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 9751fb26..074f865f 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1193,6 +1193,7 @@ def add_share(ap): ap2 = ap.add_argument_group("share-url options") ap2.add_argument("--shr", metavar="DIR", type=u, default="", help="toplevel virtual folder for shared files/folders, for example [\033[32m/share\033[0m]") ap2.add_argument("--shr-db", metavar="FILE", type=u, default=db_path, help="database to store shares in") + ap2.add_argument("--shr-who", metavar="TXT", type=u, default="auth", help="who can create a share? [\033[32mno\033[0m]=nobody, [\033[32ma\033[0m]=admin-permission, [\033[32mauth\033[0m]=authenticated (volflag=shr_who)") ap2.add_argument("--shr-adm", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to view/delete any share") ap2.add_argument("--shr-rt", metavar="MIN", type=int, default=1440, help="shares can be revived by their owner if they expired less than MIN minutes ago; [\033[32m60\033[0m]=hour, [\033[32m1440\033[0m]=day, [\033[32m10080\033[0m]=week") ap2.add_argument("--shr-v", action="store_true", help="debug") diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index f68a9f7b..69973ec5 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -976,6 +976,14 @@ class AuthSrv(object): self.indent = "" self.is_lxc = args.c == ["/z/initcfg"] + self._vf0b = { + "tcolor": self.args.tcolor, + "du_iwho": self.args.du_iwho, + "shr_who": self.args.shr_who if self.args.shr else "no", + } + self._vf0 = self._vf0b.copy() + self._vf0["d2d"] = True + # fwd-decl self.vfs = VFS(log_func, "", "", "", AXS(), {}) self.acct: dict[str, str] = {} # uname->pw @@ -1014,10 +1022,10 @@ class AuthSrv(object): yield prev, True def vf0(self): - return {"d2d": True, "tcolor": self.args.tcolor, "du_iwho": self.args.du_iwho} + return self._vf0.copy() def vf0b(self): - return {"tcolor": self.args.tcolor, "du_iwho": self.args.du_iwho} + return self._vf0b.copy() def idp_checkin( self, broker: Optional["BrokerCli"], uname: str, gname: str @@ -2298,6 +2306,9 @@ class AuthSrv(object): vol.flags["du_iwho"] = n_du_who(vol.flags["du_who"]) + if not enshare: + vol.flags["shr_who"] = "no" + if vol.flags.get("og"): self.args.uqe = True @@ -2804,6 +2815,7 @@ class AuthSrv(object): "dcrop": vf["crop"], "dth3x": vf["th3x"], "u2ts": vf["u2ts"], + "shr_who": vf["shr_who"], "frand": bool(vf.get("rand")), "lifetime": vf.get("lifetime") or 0, "unlist": vf.get("unlist") or "", @@ -2818,6 +2830,7 @@ class AuthSrv(object): "have_c2flac": self.args.allow_flac, "have_c2wav": self.args.allow_wav, "have_shr": self.args.shr, + "shr_who": vf["shr_who"], "have_zip": not self.args.no_zip, "have_mv": not self.args.no_mv, "have_del": not self.args.no_del, diff --git a/copyparty/cfg.py b/copyparty/cfg.py index 549ffa10..2fcb3d4e 100644 --- a/copyparty/cfg.py +++ b/copyparty/cfg.py @@ -108,6 +108,7 @@ def vf_vmap() -> dict[str, str]: "put_name", "mv_retry", "rm_retry", + "shr_who", "sort", "tail_fd", "tail_rate", @@ -350,6 +351,7 @@ flagcats = { "dky": 'allow seeing files (not folders) inside a specific folder\nwith "g" perm, and does not require a valid dirkey to do so', "rss": "allow '?rss' URL suffix (experimental)", "rmagic": "expensive analysis for mimetype accuracy", + "shr_who=auth": "who can create shares? no/auth/a", "unp_who=2": "unpost only if same... 1=ip+name, 2=ip, 3=name", "ups_who=2": "restrict viewing the list of recent uploads", "zip_who=2": "restrict access to download-as-zip/tar", diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index dd4d958e..d02efd0a 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -5955,6 +5955,14 @@ class HttpCli(object): except: raise Pebkac(400, "you dont have all the perms you tried to grant") + zs = vfs.flags["shr_who"] + if zs == "auth" and self.uname != "*": + pass + elif zs == "a" and self.uname in vfs.axs.uadmin: + pass + else: + raise Pebkac(400, "you dont have perms to create shares from this volume") + ap, reals, _ = vfs.ls( rem, self.uname, not self.args.no_scandir, [[s_rd, s_wr, s_mv, s_del]] ) diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index 6c1f6184..c6cac72e 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -11807,6 +11807,7 @@ var ACtx = !IPHONE && (window.AudioContext || window.webkitAudioContext), noih = /[?&]v\b/.exec(sloc0), dbg_kbd = /[?&]dbgkbd\b/.exec(sloc0), abrt_key = "", + can_shr = false, rtt = null, ldks = [], dks = {}, @@ -14396,7 +14397,7 @@ var fileman = (function () { hdel = !(have_del && has(perms, 'delete')), hcut = !(have_mv && has(perms, 'move')), hpst = !(have_mv && has(perms, 'write')), - hshr = !(have_shr && acct != '*' && (has(perms, 'read') || has(perms, 'write'))); + hshr = !can_shr; if (!(enren || endel || encut || enpst)) hren = hdel = hcut = hpst = true; @@ -17887,7 +17888,7 @@ var treectl = (function () { fun(); } - if (window.have_shr && QS('#op_unpost.act') && (cdir.startsWith(SR + have_shr) || get_evpath().startsWith(SR + have_shr))) + if (can_shr && QS('#op_unpost.act') && (cdir.startsWith(SR + have_shr) || get_evpath().startsWith(SR + have_shr))) goto('unpost'); } @@ -18299,9 +18300,10 @@ function apply_perms(res) { if (konmai < 0) { acct = 'Ted Faro'; srvinf = 'FAS Nexus // 57.3 EiB free of 127 EiB'; + res.shr_who = 'auth'; perms = res.perms = chk; have_up2k_idx = have_tags_idx = 1; - have_shr = have_mv = have_del = true; + have_mv = have_del = true; } var a = QS('#ops a[data-dest="up2k"]'); @@ -18366,6 +18368,11 @@ function apply_perms(res) { de = document.documentElement, tds = QSA('#u2conf td'); + shr_who = res.shr_who || shr_who; + can_shr = acct != '*' && (have_read || have_write) && ( + (shr_who == 'a' && has(perms, 'admin')) || + (shr_who == 'auth')); + clmod(de, "read", have_read); clmod(de, "write", have_write); clmod(de, "nread", !have_read); diff --git a/copyparty/web/up2k.js b/copyparty/web/up2k.js index a52dd172..dc2e8243 100644 --- a/copyparty/web/up2k.js +++ b/copyparty/web/up2k.js @@ -3427,6 +3427,7 @@ if (QS('#op_up2k.act')) goto_up2k(); apply_perms({ "perms": perms, "frand": frand, "u2ts": u2ts }); +fileman.render(); (function () { diff --git a/tests/util.py b/tests/util.py index 6acc4a42..cfb5979d 100644 --- a/tests/util.py +++ b/tests/util.py @@ -208,6 +208,7 @@ class Cfg(Namespace): rm_retry="0/0", s_rd_sz=256 * 1024, s_wr_sz=256 * 1024, + shr_who="auth", sort="href", srch_hits=99999, SRS="/",