Skip to content

Commit

Permalink
add permission "a" to show uploader IPs (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
9001 committed Jul 12, 2023
1 parent b54b721 commit 551d99b
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 33 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ permissions:
* `d` (delete): delete files/folders
* `g` (get): only download files, cannot see folder contents or zip/tar
* `G` (upget): same as `g` except uploaders get to see their own filekeys (see `fk` in examples below)
* `a` (admin): can see uploader IPs

examples:
* add accounts named u1, u2, u3 with passwords p1, p2, p3: `-a u1:p1 -a u2:p2 -a u3:p3`
Expand Down
1 change: 1 addition & 0 deletions contrib/nixos/modules/copyparty.nix
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ in {
"d" (delete): permanently delete files and folders
"g" (get): download files, but cannot see folder contents
"G" (upget): "get", but can see filekeys of their own uploads
"a" (upget): can see uploader IPs
For example: "rwmd"
Expand Down
7 changes: 3 additions & 4 deletions copyparty/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ def get_sects():
"d" (delete): permanently delete files and folders
"g" (get): download files, but cannot see folder contents
"G" (upget): "get", but can see filekeys of their own uploads
"a" (admin): can see uploader IPs
too many volflags to list here, see --help-flags
Expand Down Expand Up @@ -1073,7 +1074,7 @@ def add_db_metadata(ap):
ap2.add_argument("--mtag-vv", action="store_true", help="debug mtp settings and mutagen/ffprobe parsers")
ap2.add_argument("-mtm", metavar="M=t,t,t", type=u, action="append", help="add/replace metadata mapping")
ap2.add_argument("-mte", metavar="M,M,M", type=u, help="tags to index/display (comma-sep.)",
default="circle,album,.tn,artist,title,.bpm,key,.dur,.q,.vq,.aq,vc,ac,fmt,res,.fps,ahash,vhash")
default="circle,album,.tn,artist,title,.bpm,key,.dur,.q,.vq,.aq,vc,ac,fmt,res,.fps,ahash,vhash,up_ip,.up_at")
ap2.add_argument("-mth", metavar="M,M,M", type=u, help="tags to hide by default (comma-sep.)",
default=".vq,.aq,vc,ac,fmt,res,.fps")
ap2.add_argument("-mtp", metavar="M=[f,]BIN", type=u, action="append", help="read tag M using program BIN to parse the file")
Expand Down Expand Up @@ -1337,11 +1338,9 @@ def main(argv: Optional[list[str]] = None) -> None:
if re.match("c[^,]", opt):
mod = True
na.append("c," + opt[1:])
elif re.sub("^[rwmdgG]*", "", opt) and "," not in opt:
elif re.sub("^[rwmdgGa]*", "", opt) and "," not in opt:
mod = True
perm = opt[0]
if perm == "a":
perm = "rw"
na.append(perm + "," + opt[1:])
else:
na.append(opt)
Expand Down
51 changes: 35 additions & 16 deletions copyparty/authsrv.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,19 @@ def __init__(
udel: Optional[Union[list[str], set[str]]] = None,
uget: Optional[Union[list[str], set[str]]] = None,
upget: Optional[Union[list[str], set[str]]] = None,
uadmin: 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 [])
self.uadmin: set[str] = set(uadmin or [])

def __repr__(self) -> str:
return "AXS(%s)" % (
", ".join(
"%s=%r" % (k, self.__dict__[k])
for k in "uread uwrite umove udel uget upget".split()
)
)
ks = "uread uwrite umove udel uget upget uadmin".split()
return "AXS(%s)" % (", ".join("%s=%r" % (k, self.__dict__[k]) for k in ks),)


class Lim(object):
Expand Down Expand Up @@ -435,8 +433,8 @@ def _find(self, vpath: str) -> tuple["VFS", str]:

def can_access(
self, vpath: str, uname: str
) -> tuple[bool, bool, bool, bool, bool, bool]:
"""can Read,Write,Move,Delete,Get,Upget"""
) -> tuple[bool, bool, bool, bool, bool, bool, bool]:
"""can Read,Write,Move,Delete,Get,Upget,Admin"""
if vpath:
vn, _ = self._find(undot(vpath))
else:
Expand All @@ -450,6 +448,7 @@ def can_access(
uname in c.udel or "*" in c.udel,
uname in c.uget or "*" in c.uget,
uname in c.upget or "*" in c.upget,
uname in c.uadmin or "*" in c.uadmin,
)

def get(
Expand Down Expand Up @@ -944,7 +943,7 @@ def _parse_config_file(
try:
self._l(ln, 5, "volume access config:")
sk, sv = ln.split(":")
if re.sub("[rwmdgG]", "", sk) or not sk:
if re.sub("[rwmdgGa]", "", sk) or not sk:
err = "invalid accs permissions list; "
raise Exception(err)
if " " in re.sub(", *", "", sv).strip():
Expand All @@ -953,7 +952,7 @@ def _parse_config_file(
self._read_vol_str(sk, sv.replace(" ", ""), daxs[vp], mflags[vp])
continue
except:
err += "accs entries must be 'rwmdgG: user1, user2, ...'"
err += "accs entries must be 'rwmdgGa: user1, user2, ...'"
raise Exception(err)

if cat == catf:
Expand Down Expand Up @@ -989,7 +988,7 @@ def _parse_config_file(
def _read_vol_str(
self, lvl: str, uname: str, axs: AXS, flags: dict[str, Any]
) -> None:
if lvl.strip("crwmdgG"):
if lvl.strip("crwmdgGa"):
raise Exception("invalid volflag: {},{}".format(lvl, uname))

if lvl == "c":
Expand Down Expand Up @@ -1021,6 +1020,7 @@ def _read_vol_str(
("g", axs.uget),
("G", axs.uget),
("G", axs.upget),
("a", axs.uadmin),
]: # b bb bbb
if ch in lvl:
if un == "*":
Expand Down Expand Up @@ -1092,7 +1092,7 @@ def reload(self) -> None:

if self.args.v:
# list of src:dst:permset:permset:...
# permset is <rwmdgG>[,username][,username] or <c>,<flag>[=args]
# permset is <rwmdgGa>[,username][,username] or <c>,<flag>[=args]
for v_str in self.args.v:
m = re_vol.match(v_str)
if not m:
Expand Down Expand Up @@ -1196,7 +1196,15 @@ def reload(self) -> None:
all_users = {}
missing_users = {}
for axs in daxs.values():
for d in [axs.uread, axs.uwrite, axs.umove, axs.udel, axs.uget, axs.upget]:
for d in [
axs.uread,
axs.uwrite,
axs.umove,
axs.udel,
axs.uget,
axs.upget,
axs.uadmin,
]:
for usr in d:
all_users[usr] = 1
if usr != "*" and usr not in acct:
Expand Down Expand Up @@ -1611,6 +1619,7 @@ def reload(self) -> None:
["delete", "udel"],
[" get", "uget"],
[" upget", "upget"],
["uadmin", "uadmin"],
]:
u = list(sorted(getattr(zv.axs, attr)))
u = ", ".join("\033[35meverybody\033[0m" if x == "*" else x for x in u)
Expand Down Expand Up @@ -1756,10 +1765,19 @@ def dbg_ls(self) -> None:
raise Exception("volume not found: " + zs)

self.log(str({"users": users, "vols": vols, "flags": flags}))
t = "/{}: read({}) write({}) move({}) del({}) get({}) upget({})"
t = "/{}: read({}) write({}) move({}) del({}) get({}) upget({}) uadmin({})"
for k, zv in self.vfs.all_vols.items():
vc = zv.axs
vs = [k, vc.uread, vc.uwrite, vc.umove, vc.udel, vc.uget, vc.upget]
vs = [
k,
vc.uread,
vc.uwrite,
vc.umove,
vc.udel,
vc.uget,
vc.upget,
vc.uadmin,
]
self.log(t.format(*vs))

flag_v = "v" in flags
Expand Down Expand Up @@ -1898,6 +1916,7 @@ def cgen(self) -> None:
"d": "udel",
"g": "uget",
"G": "upget",
"a": "uadmin",
}
users = {}
for pkey in perms.values():
Expand Down Expand Up @@ -2094,7 +2113,7 @@ def upgrade_cfg_fmt(
else:
sn = sn.replace(",", ", ")
ret.append(" " + sn)
elif sn[:1] in "rwmdgG":
elif sn[:1] in "rwmdgGa":
if cat != catx:
cat = catx
ret.append(cat)
Expand Down
4 changes: 3 additions & 1 deletion copyparty/ftpd.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ def __init__(

self.can_read = self.can_write = self.can_move = False
self.can_delete = self.can_get = self.can_upget = False
self.can_admin = False

self.listdirinfo = self.listdir
self.chdir(".")
Expand Down Expand Up @@ -168,7 +169,7 @@ def v2a(
if not avfs:
raise FSE(t.format(vpath), 1)

cr, cw, cm, cd, _, _ = avfs.can_access("", self.h.uname)
cr, cw, cm, cd, _, _, _ = avfs.can_access("", self.h.uname)
if r and not cr or w and not cw or m and not cm or d and not cd:
raise FSE(t.format(vpath), 1)

Expand Down Expand Up @@ -243,6 +244,7 @@ def chdir(self, path: str) -> None:
self.can_delete,
self.can_get,
self.can_upget,
self.can_admin,
) = avfs.can_access("", self.h.uname)

def mkdir(self, path: str) -> None:
Expand Down
22 changes: 19 additions & 3 deletions copyparty/httpcli.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ def __init__(self, conn: "HttpConn") -> None:
self.can_delete = False
self.can_get = False
self.can_upget = False
self.can_admin = False
# post
self.parser: Optional[MultipartParser] = None
# end placeholders
Expand Down Expand Up @@ -431,6 +432,7 @@ def run(self) -> bool:
self.can_delete,
self.can_get,
self.can_upget,
self.can_admin,
) = (
avn.can_access("", self.uname) if avn else [False] * 6
)
Expand Down Expand Up @@ -782,6 +784,7 @@ def handle_get(self) -> bool:
self.log("plugin override; access permitted")
self.can_read = self.can_write = self.can_move = True
self.can_delete = self.can_get = self.can_upget = True
self.can_admin = True
else:
return self.tx_404(True)
else:
Expand Down Expand Up @@ -3535,6 +3538,8 @@ def tx_browser(self) -> bool:
perms.append("get")
if self.can_upget:
perms.append("upget")
if self.can_admin:
perms.append("admin")

url_suf = self.urlq({}, ["k"])
is_ls = "ls" in self.uparam
Expand Down Expand Up @@ -3786,22 +3791,33 @@ def tx_browser(self) -> bool:
if vn != dbv:
_, rd = vn.get_dbv(rd)

erd_efn = (rd, fn)
q = "select mt.k, mt.v from up inner join mt on mt.w = substr(up.w,1,16) where up.rd = ? and up.fn = ? and +mt.k != 'x'"
try:
r = icur.execute(q, (rd, fn))
r = icur.execute(q, erd_efn)
except Exception as ex:
if "database is locked" in str(ex):
break

try:
args = s3enc(idx.mem_cur, rd, fn)
r = icur.execute(q, args)
erd_efn = s3enc(idx.mem_cur, rd, fn)
r = icur.execute(q, erd_efn)
except:
t = "tag read error, {}/{}\n{}"
self.log(t.format(rd, fn, min_ex()))
break

fe["tags"] = {k: v for k, v in r}

if self.can_admin:
q = "select ip, at from up where rd=? and fn=?"
try:
zs1, zs2 = icur.execute(q, erd_efn).fetchone()
fe["tags"]["up_ip"] = zs1
fe["tags"][".up_at"] = zs2
except:
pass

_ = [tagset.add(k) for k in fe["tags"]]

if icur:
Expand Down
16 changes: 10 additions & 6 deletions copyparty/web/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -5780,14 +5780,18 @@ var treectl = (function () {

for (var b = 0; b < res.taglist.length; b++) {
var k = res.taglist[b],
v = (tn.tags || {})[k] || "";
v = (tn.tags || {})[k] || "",
sv = null;

if (k == ".dur") {
var sv = v ? s2ms(v) : "";
ln[ln.length - 1] += '</td><td sortv="' + v + '">' + sv;
if (k == ".dur")
sv = v ? s2ms(v) : "";
else if (k == ".up_at")
sv = v ? unix2iso(v) : "";
else {
ln.push(v);
continue;
}
ln.push(v);
ln[ln.length - 1] += '</td><td sortv="' + v + '">' + sv;
}
ln = ln.concat([tn.ext, unix2iso(tn.ts)]).join('</td><td>');
html.push(ln + '</td></tr>');
Expand Down Expand Up @@ -6066,7 +6070,7 @@ function apply_perms(res) {

var axs = [],
aclass = '>',
chk = ['read', 'write', 'move', 'delete', 'get'];
chk = ['read', 'write', 'move', 'delete', 'get', 'admin'];

for (var a = 0; a < chk.length; a++)
if (has(perms, chk[a]))
Expand Down
6 changes: 3 additions & 3 deletions tests/test_vfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,9 @@ def test(self):
self.assertEqual(n.realpath, os.path.join(td, "a"))
self.assertAxs(n.axs.uread, ["*"])
self.assertAxs(n.axs.uwrite, [])
perm_na = (False, False, False, False, False, False)
perm_rw = (True, True, False, False, False, False)
perm_ro = (True, False, False, False, False, False)
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)
self.assertEqual(vfs.can_access("/", "*"), perm_na)
self.assertEqual(vfs.can_access("/", "k"), perm_rw)
self.assertEqual(vfs.can_access("/a", "*"), perm_ro)
Expand Down

0 comments on commit 551d99b

Please sign in to comment.