add --auth-ord; closes #689

This commit is contained in:
ed 2025-08-26 23:33:53 +00:00
parent d30240b431
commit 543b7ea959
5 changed files with 66 additions and 6 deletions

View file

@ -676,6 +676,42 @@ def get_sects():
""" """
), ),
], ],
[
"auth-ord",
"authentication precedence",
dedent(
"""
\033[33m--auth-ord\033[0m is a comma-separated list of auth options
(one or more of the [\033[35moptions\033[0m] below); first one wins
[\033[35mpw\033[0m] is conventional login, for example the "\033[36mPW\033[0m" header,
or the \033[36m?pw=\033[0m[...] URL-suffix, or a valid session cookie
(see \033[33m--help-auth\033[0m)
[\033[35midp\033[0m] is a username provided in the http-request-header
defined by \033[33m--idp-h-usr\033[0m and/or \033[33m--idp-hm-usr\033[0m, which is
provided by an authentication middleware such as
authentik, authelia, tailscale, ... (see \033[33m--help-idp\033[0m)
[\033[35midp-h\033[0m] is specifically the \033[33m--idp-h-usr\033[0m header,
[\033[35midp-hm\033[0m] is specifically an \033[33m--idp-hm-usr\033[0m header;
[\033[35midp\033[0m] is the same as [\033[35midp-hm,idp-h\033[0m]
[\033[35mipu\033[0m] is a mapping from an IP-address to a username,
auto-authing that client-IP to that account
(see the description of \033[36m--ipu\033[0m in \033[33m--help\033[0m)
NOTE: even if an option (\033[35mpw\033[0m/\033[35mipu\033[0m/...) is not in the list,
it may still be enabled and can still take effect if
none of the other alternatives identify the user
NOTE: if [\033[35mipu\033[0m] is in the list, it must be FIRST or LAST
NOTE: if [\033[35mpw\033[0m] is not in the list, the logout-button
will be hidden when any idp feature is enabled
"""
),
],
[ [
"flags", "flags",
"list of volflags", "list of volflags",
@ -1254,6 +1290,7 @@ def add_auth(ap):
ap2.add_argument("--idp-store", metavar="N", type=int, default=1, help="how to use \033[33m--idp-db\033[0m; [\033[32m0\033[0m] = entirely disable, [\033[32m1\033[0m] = write-only (effectively disabled), [\033[32m2\033[0m] = remember users, [\033[32m3\033[0m] = remember users and groups.\nNOTE: Will remember and restore the IdP-volumes of all users for all eternity if set to 2 or 3, even when user is deleted from your IdP") ap2.add_argument("--idp-store", metavar="N", type=int, default=1, help="how to use \033[33m--idp-db\033[0m; [\033[32m0\033[0m] = entirely disable, [\033[32m1\033[0m] = write-only (effectively disabled), [\033[32m2\033[0m] = remember users, [\033[32m3\033[0m] = remember users and groups.\nNOTE: Will remember and restore the IdP-volumes of all users for all eternity if set to 2 or 3, even when user is deleted from your IdP")
ap2.add_argument("--idp-adm", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to use /?idp (the cache management UI)") ap2.add_argument("--idp-adm", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to use /?idp (the cache management UI)")
ap2.add_argument("--idp-cookie", metavar="S", type=int, default=0, help="generate a session-token for IdP users which is written to cookie \033[33mcppws\033[0m (or \033[33mcppwd\033[0m if plaintext), to reduce the load on the IdP server, lifetime \033[33mS\033[0m seconds.\n └─note: The expiration time is a client hint only; the actual lifetime of the session-token is infinite (until next restart with \033[33m--ses-db\033[0m wiped)") ap2.add_argument("--idp-cookie", metavar="S", type=int, default=0, help="generate a session-token for IdP users which is written to cookie \033[33mcppws\033[0m (or \033[33mcppwd\033[0m if plaintext), to reduce the load on the IdP server, lifetime \033[33mS\033[0m seconds.\n └─note: The expiration time is a client hint only; the actual lifetime of the session-token is infinite (until next restart with \033[33m--ses-db\033[0m wiped)")
ap2.add_argument("--auth-ord", metavar="TXT", type=u, default="idp,ipu", help="controls auth precedence; examples: [\033[32mpw,idp,ipu\033[0m], [\033[32mipu,pw,idp\033[0m], see --help-auth-ord")
ap2.add_argument("--no-bauth", action="store_true", help="disable basic-authentication support; do not accept passwords from the 'Authenticate' header at all. NOTE: This breaks support for the android app") ap2.add_argument("--no-bauth", action="store_true", help="disable basic-authentication support; do not accept passwords from the 'Authenticate' header at all. NOTE: This breaks support for the android app")
ap2.add_argument("--bauth-last", action="store_true", help="keeps basic-authentication enabled, but only as a last-resort; if a cookie is also provided then the cookie wins") ap2.add_argument("--bauth-last", action="store_true", help="keeps basic-authentication enabled, but only as a last-resort; if a cookie is also provided then the cookie wins")
ap2.add_argument("--ses-db", metavar="PATH", type=u, default=ses_db, help="where to store the sessions database (if you run multiple copyparty instances, make sure they use different DBs)") ap2.add_argument("--ses-db", metavar="PATH", type=u, default=ses_db, help="where to store the sessions database (if you run multiple copyparty instances, make sure they use different DBs)")
@ -1264,6 +1301,10 @@ def add_auth(ap):
ap2.add_argument("--ipr", metavar="CIDR=USR", type=u, action="append", help="\033[34mREPEATABLE:\033[0m username \033[33mUSR\033[0m can only connect from an IP matching one or more \033[33mCIDR\033[0m (comma-sep.); example: [\033[32m192.168.123.0/24,172.16.0.0/16=dave]") ap2.add_argument("--ipr", metavar="CIDR=USR", type=u, action="append", help="\033[34mREPEATABLE:\033[0m username \033[33mUSR\033[0m can only connect from an IP matching one or more \033[33mCIDR\033[0m (comma-sep.); example: [\033[32m192.168.123.0/24,172.16.0.0/16=dave]")
ap2.add_argument("--have-idp-hdrs", type=u, default="", help=argparse.SUPPRESS) ap2.add_argument("--have-idp-hdrs", type=u, default="", help=argparse.SUPPRESS)
ap2.add_argument("--have-ipu-or-ipr", type=u, default="", help=argparse.SUPPRESS) ap2.add_argument("--have-ipu-or-ipr", type=u, default="", help=argparse.SUPPRESS)
ap2.add_argument("--ao-idp-before-pw", type=u, default="", help=argparse.SUPPRESS)
ap2.add_argument("--ao-h-before-hm", type=u, default="", help=argparse.SUPPRESS)
ap2.add_argument("--ao-ipu-wins", type=u, default="", help=argparse.SUPPRESS)
ap2.add_argument("--ao-has-pw", type=u, default="", help=argparse.SUPPRESS)
def add_chpw(ap): def add_chpw(ap):

View file

@ -1713,6 +1713,7 @@ class AuthSrv(object):
self.args.have_idp_hdrs = bool(self.args.idp_h_usr or self.args.idp_hm_usr) self.args.have_idp_hdrs = bool(self.args.idp_h_usr or self.args.idp_hm_usr)
self.args.have_ipu_or_ipr = bool(self.args.ipu or self.args.ipr) self.args.have_ipu_or_ipr = bool(self.args.ipu or self.args.ipr)
self.setup_auth_ord()
self.setup_pwhash(acct) self.setup_pwhash(acct)
defpw = acct.copy() defpw = acct.copy()
@ -2864,6 +2865,18 @@ class AuthSrv(object):
zs = str(vol.flags.get("tcolor") or self.args.tcolor) zs = str(vol.flags.get("tcolor") or self.args.tcolor)
vol.flags["tcolor"] = zs.lstrip("#") vol.flags["tcolor"] = zs.lstrip("#")
def setup_auth_ord(self) -> None:
ao = [x.strip() for x in self.args.auth_ord.split(",")]
if "idp" in ao:
zi = ao.index("idp")
ao = ao[:zi] + ["idp-hm", "idp-h"] + ao[zi:]
zsl = "pw idp-h idp-hm ipu".split()
pw, h, hm, ipu = [ao.index(x) if x in ao else 99 for x in zsl]
self.args.ao_idp_before_pw = min(h, hm) < pw
self.args.ao_h_before_hm = h < hm
self.args.ao_ipu_wins = ipu == 0
self.args.ao_have_pw = pw < 99
def load_idp_db(self, quiet=False) -> None: def load_idp_db(self, quiet=False) -> None:
# mutex me # mutex me
level = self.args.idp_store level = self.args.idp_store

View file

@ -624,7 +624,9 @@ class HttpCli(object):
or "*" or "*"
) )
if self.args.have_idp_hdrs: if self.args.have_idp_hdrs and (
self.uname == "*" or self.args.ao_idp_before_pw
):
idp_usr = "" idp_usr = ""
if self.args.idp_hm_usr: if self.args.idp_hm_usr:
for hn, hmv in self.args.idp_hm_usr_p.items(): for hn, hmv in self.args.idp_hm_usr_p.items():
@ -637,9 +639,9 @@ class HttpCli(object):
if idp_usr: if idp_usr:
break break
for hn in self.args.idp_h_usr: for hn in self.args.idp_h_usr:
if idp_usr: if idp_usr and not self.args.ao_h_before_hm:
break break
idp_usr = self.headers.get(hn) idp_usr = self.headers.get(hn) or idp_usr
if idp_usr: if idp_usr:
idp_grp = ( idp_grp = (
self.headers.get(self.args.idp_h_grp) or "" self.headers.get(self.args.idp_h_grp) or ""
@ -688,7 +690,10 @@ class HttpCli(object):
if idp_usr in self.asrv.vfs.aread: if idp_usr in self.asrv.vfs.aread:
self.pw = "" self.pw = ""
self.uname = idp_usr self.uname = idp_usr
self.html_head += "<script>var is_idp=1</script>\n" if self.args.ao_have_pw:
self.html_head += "<script>var is_idp=1</script>\n"
else:
self.html_head += "<script>var is_idp=2</script>\n"
zs = self.asrv.ases.get(idp_usr) zs = self.asrv.ases.get(idp_usr)
if zs: if zs:
self.set_idp_cookie(zs) self.set_idp_cookie(zs)
@ -696,7 +701,7 @@ class HttpCli(object):
self.log("unknown username: %r" % (idp_usr,), 1) self.log("unknown username: %r" % (idp_usr,), 1)
if self.args.have_ipu_or_ipr: if self.args.have_ipu_or_ipr:
if self.args.ipu and self.uname == "*": if self.args.ipu and (self.uname == "*" or self.args.ao_ipu_wins):
self.uname = self.conn.ipu_iu[self.conn.ipu_nm.map(self.ip)] self.uname = self.conn.ipu_iu[self.conn.ipu_nm.map(self.ip)]
ipr = self.conn.hsrv.ipr ipr = self.conn.hsrv.ipr
if ipr and self.uname in ipr: if ipr and self.uname in ipr:

View file

@ -834,7 +834,7 @@ if (o1 && o2 && d.lo3)
o1.setAttribute("value", d.lo3.format(o2.textContent)); o1.setAttribute("value", d.lo3.format(o2.textContent));
try { try {
if (is_idp) { if (is_idp > 1) {
var z = ['#l+div', '#l', '#c']; var z = ['#l+div', '#l', '#c'];
for (var a = 0; a < z.length; a++) for (var a = 0; a < z.length; a++)
QS(z[a]).style.display = 'none'; QS(z[a]).style.display = 'none';

View file

@ -183,6 +183,7 @@ class Cfg(Namespace):
v=v or [], v=v or [],
c=c, c=c,
E=E, E=E,
auth_ord="idp,ipu",
bup_ck="sha512", bup_ck="sha512",
chmod_d="755", chmod_d="755",
cookie_cmax=8192, cookie_cmax=8192,