mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 00:52:16 -06:00
new access level g
This commit is contained in:
parent
12b7317831
commit
5849c446ed
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -55,4 +55,5 @@
|
|||
"py27"
|
||||
],
|
||||
"python.linting.enabled": true,
|
||||
"python.pythonPath": "/usr/bin/python3"
|
||||
}
|
|
@ -257,6 +257,7 @@ permissions:
|
|||
* `w` (write): upload files, move files *into* this folder
|
||||
* `m` (move): move files/folders *from* this folder
|
||||
* `d` (delete): delete files/folders
|
||||
* `g` (get): only download files, cannot see folder contents or zip/tar
|
||||
|
||||
examples:
|
||||
* add accounts named u1, u2, u3 with passwords p1, p2, p3: `-a u1:p1 -a u2:p2 -a u3:p3`
|
||||
|
|
|
@ -219,6 +219,7 @@ def run_argparse(argv, formatter):
|
|||
"w" (write): upload files; need "r" to see the uploads
|
||||
"m" (move): move files and folders; need "w" at destination
|
||||
"d" (delete): permanently delete files and folders
|
||||
"g" (get): download files, but cannot see folder contents
|
||||
|
||||
too many volflags to list here, see the other sections
|
||||
|
||||
|
@ -493,7 +494,7 @@ def main(argv=None):
|
|||
if re.match("c[^,]", opt):
|
||||
mod = True
|
||||
na.append("c," + opt[1:])
|
||||
elif re.sub("^[rwmd]*", "", opt) and "," not in opt:
|
||||
elif re.sub("^[rwmdg]*", "", opt) and "," not in opt:
|
||||
mod = True
|
||||
perm = opt[0]
|
||||
if perm == "a":
|
||||
|
|
|
@ -29,17 +29,18 @@ LEELOO_DALLAS = "leeloo_dallas"
|
|||
|
||||
|
||||
class AXS(object):
|
||||
def __init__(self, uread=None, uwrite=None, umove=None, udel=None):
|
||||
def __init__(self, uread=None, uwrite=None, umove=None, udel=None, uget=None):
|
||||
self.uread = {} if uread is None else {k: 1 for k in uread}
|
||||
self.uwrite = {} if uwrite is None else {k: 1 for k in uwrite}
|
||||
self.umove = {} if umove is None else {k: 1 for k in umove}
|
||||
self.udel = {} if udel is None else {k: 1 for k in udel}
|
||||
self.uget = {} if uget is None else {k: 1 for k in uget}
|
||||
|
||||
def __repr__(self):
|
||||
return "AXS({})".format(
|
||||
", ".join(
|
||||
"{}={!r}".format(k, self.__dict__[k])
|
||||
for k in "uread uwrite umove udel".split()
|
||||
for k in "uread uwrite umove udel uget".split()
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -215,6 +216,7 @@ class VFS(object):
|
|||
self.awrite = {}
|
||||
self.amove = {}
|
||||
self.adel = {}
|
||||
self.aget = {}
|
||||
else:
|
||||
self.histpath = None
|
||||
self.all_vols = None
|
||||
|
@ -222,6 +224,7 @@ class VFS(object):
|
|||
self.awrite = None
|
||||
self.amove = None
|
||||
self.adel = None
|
||||
self.aget = None
|
||||
|
||||
def __repr__(self):
|
||||
return "VFS({})".format(
|
||||
|
@ -308,7 +311,7 @@ class VFS(object):
|
|||
|
||||
def can_access(self, vpath, uname):
|
||||
# type: (str, str) -> tuple[bool, bool, bool, bool]
|
||||
"""can Read,Write,Move,Delete"""
|
||||
"""can Read,Write,Move,Delete,Get"""
|
||||
vn, _ = self._find(vpath)
|
||||
c = vn.axs
|
||||
return [
|
||||
|
@ -316,10 +319,20 @@ class VFS(object):
|
|||
uname in c.uwrite or "*" in c.uwrite,
|
||||
uname in c.umove or "*" in c.umove,
|
||||
uname in c.udel or "*" in c.udel,
|
||||
uname in c.uget or "*" in c.uget,
|
||||
]
|
||||
|
||||
def get(self, vpath, uname, will_read, will_write, will_move=False, will_del=False):
|
||||
# type: (str, str, bool, bool, bool, bool) -> tuple[VFS, str]
|
||||
def get(
|
||||
self,
|
||||
vpath,
|
||||
uname,
|
||||
will_read,
|
||||
will_write,
|
||||
will_move=False,
|
||||
will_del=False,
|
||||
will_get=False,
|
||||
):
|
||||
# type: (str, str, bool, bool, bool, bool, bool) -> tuple[VFS, str]
|
||||
"""returns [vfsnode,fs_remainder] if user has the requested permissions"""
|
||||
vn, rem = self._find(vpath)
|
||||
c = vn.axs
|
||||
|
@ -329,6 +342,7 @@ class VFS(object):
|
|||
[will_write, c.uwrite, "write"],
|
||||
[will_move, c.umove, "move"],
|
||||
[will_del, c.udel, "delete"],
|
||||
[will_get, c.uget, "get"],
|
||||
]:
|
||||
if req and (uname not in d and "*" not in d) and uname != LEELOO_DALLAS:
|
||||
m = "you don't have {}-access for this location"
|
||||
|
@ -368,7 +382,7 @@ class VFS(object):
|
|||
for name, vn2 in sorted(self.nodes.items()):
|
||||
ok = False
|
||||
axs = vn2.axs
|
||||
axs = [axs.uread, axs.uwrite, axs.umove, axs.udel]
|
||||
axs = [axs.uread, axs.uwrite, axs.umove, axs.udel, axs.uget]
|
||||
for pset in permsets:
|
||||
ok = True
|
||||
for req, lst in zip(pset, axs):
|
||||
|
@ -561,7 +575,7 @@ class AuthSrv(object):
|
|||
|
||||
def _read_vol_str(self, lvl, uname, axs, flags):
|
||||
# type: (str, str, AXS, any) -> None
|
||||
if lvl.strip("crwmd"):
|
||||
if lvl.strip("crwmdg"):
|
||||
raise Exception("invalid volume flag: {},{}".format(lvl, uname))
|
||||
|
||||
if lvl == "c":
|
||||
|
@ -588,6 +602,9 @@ class AuthSrv(object):
|
|||
if "d" in lvl:
|
||||
axs.udel[un] = 1
|
||||
|
||||
if "g" in lvl:
|
||||
axs.uget[un] = 1
|
||||
|
||||
def _read_volflag(self, flags, name, value, is_list):
|
||||
if name not in ["mtp"]:
|
||||
flags[name] = value
|
||||
|
@ -625,7 +642,7 @@ class AuthSrv(object):
|
|||
|
||||
if self.args.v:
|
||||
# list of src:dst:permset:permset:...
|
||||
# permset is <rwmd>[,username][,username] or <c>,<flag>[=args]
|
||||
# permset is <rwmdg>[,username][,username] or <c>,<flag>[=args]
|
||||
for v_str in self.args.v:
|
||||
m = re_vol.match(v_str)
|
||||
if not m:
|
||||
|
@ -692,20 +709,21 @@ class AuthSrv(object):
|
|||
vfs.all_vols = {}
|
||||
vfs.get_all_vols(vfs.all_vols)
|
||||
|
||||
for perm in "read write move del".split():
|
||||
for perm in "read write move del get".split():
|
||||
axs_key = "u" + perm
|
||||
unames = ["*"] + list(acct.keys())
|
||||
umap = {x: [] for x in unames}
|
||||
for usr in unames:
|
||||
for mp, vol in vfs.all_vols.items():
|
||||
if usr in getattr(vol.axs, axs_key):
|
||||
axs = getattr(vol.axs, axs_key)
|
||||
if usr in axs or "*" in axs:
|
||||
umap[usr].append(mp)
|
||||
setattr(vfs, "a" + perm, umap)
|
||||
|
||||
all_users = {}
|
||||
missing_users = {}
|
||||
for axs in daxs.values():
|
||||
for d in [axs.uread, axs.uwrite, axs.umove, axs.udel]:
|
||||
for d in [axs.uread, axs.uwrite, axs.umove, axs.udel, axs.uget]:
|
||||
for usr in d.keys():
|
||||
all_users[usr] = 1
|
||||
if usr != "*" and usr not in acct:
|
||||
|
@ -930,6 +948,7 @@ class AuthSrv(object):
|
|||
[" write", "uwrite"],
|
||||
[" move", "umove"],
|
||||
["delete", "udel"],
|
||||
[" get", "uget"],
|
||||
]:
|
||||
u = list(sorted(getattr(v.axs, attr).keys()))
|
||||
u = ", ".join("\033[35meverybody\033[0m" if x == "*" else x for x in u)
|
||||
|
@ -997,10 +1016,10 @@ class AuthSrv(object):
|
|||
raise Exception("volume not found: " + v)
|
||||
|
||||
self.log({"users": users, "vols": vols, "flags": flags})
|
||||
m = "/{}: read({}) write({}) move({}) del({})"
|
||||
m = "/{}: read({}) write({}) move({}) del({}) get({})"
|
||||
for k, v in self.vfs.all_vols.items():
|
||||
vc = v.axs
|
||||
self.log(m.format(k, vc.uread, vc.uwrite, vc.umove, vc.udel))
|
||||
self.log(m.format(k, vc.uread, vc.uwrite, vc.umove, vc.udel, vc.uget))
|
||||
|
||||
flag_v = "v" in flags
|
||||
flag_ln = "ln" in flags
|
||||
|
@ -1014,7 +1033,7 @@ class AuthSrv(object):
|
|||
for u in users:
|
||||
self.log("checking /{} as {}".format(v, u))
|
||||
try:
|
||||
vn, _ = self.vfs.get(v, u, True, False, False, False)
|
||||
vn, _ = self.vfs.get(v, u, True, False, False, False, False)
|
||||
except:
|
||||
continue
|
||||
|
||||
|
|
|
@ -213,6 +213,7 @@ class HttpCli(object):
|
|||
self.wvol = self.asrv.vfs.awrite[self.uname]
|
||||
self.mvol = self.asrv.vfs.amove[self.uname]
|
||||
self.dvol = self.asrv.vfs.adel[self.uname]
|
||||
self.gvol = self.asrv.vfs.aget[self.uname]
|
||||
|
||||
if pwd and "pw" in self.ouparam and pwd != cookies.get("cppwd"):
|
||||
self.out_headers["Set-Cookie"] = self.get_pwd_cookie(pwd)[0]
|
||||
|
@ -379,8 +380,8 @@ class HttpCli(object):
|
|||
return self.tx_file(static_path)
|
||||
|
||||
x = self.asrv.vfs.can_access(self.vpath, self.uname)
|
||||
self.can_read, self.can_write, self.can_move, self.can_delete = x
|
||||
if not self.can_read and not self.can_write:
|
||||
self.can_read, self.can_write, self.can_move, self.can_delete, self.can_get = x
|
||||
if not self.can_read and not self.can_write and not self.can_get:
|
||||
if self.vpath:
|
||||
self.log("inaccessible: [{}]".format(self.vpath))
|
||||
raise Pebkac(404)
|
||||
|
@ -1784,13 +1785,13 @@ class HttpCli(object):
|
|||
except:
|
||||
raise Pebkac(404)
|
||||
|
||||
if self.can_read:
|
||||
if rem.startswith(".hist/up2k.") or (
|
||||
rem.endswith("/dir.txt") and rem.startswith(".hist/th/")
|
||||
):
|
||||
raise Pebkac(403)
|
||||
|
||||
is_dir = stat.S_ISDIR(st.st_mode)
|
||||
if self.can_read:
|
||||
th_fmt = self.uparam.get("th")
|
||||
if th_fmt is not None:
|
||||
if is_dir:
|
||||
|
@ -1815,7 +1816,7 @@ class HttpCli(object):
|
|||
|
||||
return self.tx_ico(rem)
|
||||
|
||||
if not is_dir:
|
||||
if not is_dir and (self.can_read or self.can_get):
|
||||
if abspath.endswith(".md") and "raw" not in self.uparam:
|
||||
return self.tx_md(abspath)
|
||||
|
||||
|
@ -1859,6 +1860,8 @@ class HttpCli(object):
|
|||
perms.append("move")
|
||||
if self.can_delete:
|
||||
perms.append("delete")
|
||||
if self.can_get:
|
||||
perms.append("get")
|
||||
|
||||
url_suf = self.urlq({}, [])
|
||||
is_ls = "ls" in self.uparam
|
||||
|
@ -1928,6 +1931,9 @@ class HttpCli(object):
|
|||
if not stat.S_ISDIR(st.st_mode):
|
||||
raise Pebkac(404)
|
||||
|
||||
if "zip" in self.uparam or "tar" in self.uparam:
|
||||
raise Pebkac(403)
|
||||
|
||||
html = self.j2(tpl, **j2a)
|
||||
self.reply(html.encode("utf-8", "replace"), headers=NO_STORE)
|
||||
return True
|
||||
|
|
|
@ -227,9 +227,22 @@ function goto(dest) {
|
|||
clmod(obj[a], 'act');
|
||||
|
||||
if (dest) {
|
||||
var ui = ebi('op_' + dest);
|
||||
var ui = ebi('op_' + dest),
|
||||
lnk = QS('#ops>a[data-dest=' + dest + ']'),
|
||||
nps = lnk.getAttribute('data-perm');
|
||||
|
||||
nps = nps && nps.length ? nps.split(' ') : [];
|
||||
|
||||
if (perms.length)
|
||||
for (var a = 0; a < nps.length; a++)
|
||||
if (!has(perms, nps[a]))
|
||||
return;
|
||||
|
||||
if (!has(perms, 'read') && !has(perms, 'write') && (dest == 'up2k'))
|
||||
return;
|
||||
|
||||
clmod(ui, 'act', true);
|
||||
QS('#ops>a[data-dest=' + dest + ']').className += " act";
|
||||
lnk.className += " act";
|
||||
|
||||
var fn = window['goto_' + dest];
|
||||
if (fn)
|
||||
|
@ -3426,7 +3439,7 @@ function apply_perms(newperms) {
|
|||
|
||||
var axs = [],
|
||||
aclass = '>',
|
||||
chk = ['read', 'write', 'move', 'delete'];
|
||||
chk = ['read', 'write', 'move', 'delete', 'get'];
|
||||
|
||||
for (var a = 0; a < chk.length; a++)
|
||||
if (has(perms, chk[a]))
|
||||
|
@ -3480,7 +3493,7 @@ function apply_perms(newperms) {
|
|||
|
||||
ebi('widget').style.display = have_read ? '' : 'none';
|
||||
thegrid.setvis(have_read);
|
||||
if (!have_read)
|
||||
if (!have_read && have_write)
|
||||
goto('up2k');
|
||||
}
|
||||
|
||||
|
|
|
@ -528,7 +528,7 @@ function up2k_init(subtle) {
|
|||
got_deps = true;
|
||||
}
|
||||
|
||||
if (perms.length && !has(perms, 'read'))
|
||||
if (perms.length && !has(perms, 'read') && has(perms, 'write'))
|
||||
goto('up2k');
|
||||
|
||||
function setmsg(msg, type) {
|
||||
|
|
|
@ -98,7 +98,7 @@ class TestHttpCli(unittest.TestCase):
|
|||
if not vol.startswith(top):
|
||||
continue
|
||||
|
||||
mode = vol[-2].replace("a", "rwmd")
|
||||
mode = vol[-2].replace("a", "rw")
|
||||
usr = vol[-1]
|
||||
if usr == "a":
|
||||
usr = ""
|
||||
|
|
|
@ -197,10 +197,10 @@ class TestVFS(unittest.TestCase):
|
|||
self.assertEqual(n.realpath, os.path.join(td, "a"))
|
||||
self.assertAxs(n.axs.uread, ["*"])
|
||||
self.assertAxs(n.axs.uwrite, [])
|
||||
self.assertEqual(vfs.can_access("/", "*"), [False, False, False, False])
|
||||
self.assertEqual(vfs.can_access("/", "k"), [True, True, False, False])
|
||||
self.assertEqual(vfs.can_access("/a", "*"), [True, False, False, False])
|
||||
self.assertEqual(vfs.can_access("/a", "k"), [True, False, False, False])
|
||||
self.assertEqual(vfs.can_access("/", "*"), [False, False, False, False, False])
|
||||
self.assertEqual(vfs.can_access("/", "k"), [True, True, False, False, False])
|
||||
self.assertEqual(vfs.can_access("/a", "*"), [True, False, False, False, False])
|
||||
self.assertEqual(vfs.can_access("/a", "k"), [True, False, False, False, False])
|
||||
|
||||
# breadth-first construction
|
||||
vfs = AuthSrv(
|
||||
|
|
Loading…
Reference in a new issue