diff --git a/README.md b/README.md
index f091d122..e93e4282 100644
--- a/README.md
+++ b/README.md
@@ -1942,6 +1942,10 @@ you can disable the built-in password-based login system, and instead replace it
* the regular config-defined users will be used as a fallback for requests which don't include a valid (trusted) IdP username header
+ * `--auth-ord` configured auth precedence, for example to allow overriding the IdP with a copyparty password
+
+* the login/logout links/buttons can be replaced with links to your IdP with `--idp-login` and `--idp-logout` , for example `--idp-login /idp/login/?redir={dst}` will expand `{dst}` to the page the user was on when clicking Login
+
* if your IdP-server is slow, consider `--idp-cookie` and let requests with the cookie `cppws` bypass the IdP; experimental sessions-based feature added for a party
some popular identity providers are [Authelia](https://www.authelia.com/) (config-file based) and [authentik](https://goauthentik.io/) (GUI-based, more complex)
diff --git a/copyparty/__main__.py b/copyparty/__main__.py
index fedef803..86a2fdbe 100644
--- a/copyparty/__main__.py
+++ b/copyparty/__main__.py
@@ -897,6 +897,11 @@ def get_sects():
middleware and not by clients! and, as an extra precaution,
send a header named '\033[36mfinalmasterspark\033[0m' (a secret keyword)
and then \033[36m--idp-h-key finalmasterspark\033[0m to require that
+
+ the login/logout links/buttons can be replaced with links
+ going to your IdP's UI; \033[36m--idp-login /login/?redir={dst}\033[0m
+ will expand \033[36m{dst}\033[0m to the URL of the current page, so
+ the IdP can redirect the user back to where they were
"""
),
],
@@ -1303,6 +1308,9 @@ 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-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-login", metavar="L", type=u, default="", help="replace all login-buttons with a link to URL \033[33mL\033[0m (unless \033[32mpw\033[0m is in \033[33m--auth-ord\033[0m then both will be shown); [\033[32m{dst}\033[0m] expands to url of current page")
+ ap2.add_argument("--idp-login-t", metavar="T", type=u, default="Login with SSO", help="the label/text for the idp-login button")
+ ap2.add_argument("--idp-logout", metavar="L", type=u, default="", help="replace all logout-buttons with a link to URL \033[33mL\033[0m")
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("--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")
@@ -1317,7 +1325,7 @@ def add_auth(ap):
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)
+ ap2.add_argument("--ao-have-pw", type=u, default="", help=argparse.SUPPRESS)
def add_chpw(ap):
diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py
index 4adde56e..34a3fe46 100644
--- a/copyparty/authsrv.py
+++ b/copyparty/authsrv.py
@@ -2808,6 +2808,7 @@ class AuthSrv(object):
js_htm = {
"SPINNER": self.args.spinner,
"s_name": self.args.bname,
+ "idp_login": self.args.idp_login,
"have_up2k_idx": "e2d" in vf,
"have_acode": not self.args.no_acode,
"have_c2flac": self.args.allow_flac,
@@ -2879,7 +2880,7 @@ class AuthSrv(object):
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
+ self.args.ao_have_pw = pw < 99 or not self.args.have_idp_hdrs
def load_idp_db(self, quiet=False) -> None:
# mutex me
diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py
index 8eaed03d..8e111386 100644
--- a/copyparty/httpcli.py
+++ b/copyparty/httpcli.py
@@ -690,7 +690,7 @@ class HttpCli(object):
if idp_usr in self.asrv.vfs.aread:
self.pw = ""
self.uname = idp_usr
- if self.args.ao_have_pw:
+ if self.args.ao_have_pw or self.args.idp_logout:
self.html_head += "\n"
else:
self.html_head += "\n"
@@ -3051,7 +3051,7 @@ class HttpCli(object):
self.asrv.forget_session(self.conn.hsrv.broker, self.uname)
self.get_pwd_cookie("x")
- dst = self.args.SRS + "?h"
+ dst = self.args.idp_logout or (self.args.SRS + "?h")
h2 = 'continue'
html = self.j2s("msg", h1="ok bye", h2=h2, redir=dst)
self.reply(html.encode("utf-8"))
diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js
index 189f0a87..4106f1b6 100644
--- a/copyparty/web/browser.js
+++ b/copyparty/web/browser.js
@@ -114,6 +114,7 @@ var Ls = {
"gou": 'parent folder">up',
"gon": 'next folder">next',
"logout": "Logout ",
+ "login": "Login",
"access": " access",
"ot_close": "close submenu",
"ot_search": "search for files by attributes, path / name, music tags, or any combination of those$N$N<code>foo bar</code> = must contain both «foo» and «bar»,$N<code>foo -bar</code> = must contain «foo» but not «bar»,$N<code>^yana .opus$</code> = start with «yana» and be an «opus» file$N<code>"try unite"</code> = contain exactly «try unite»$N$Nthe date format is iso-8601, like$N<code>2009-12-31</code> or <code>2020-09-12 23:30:00</code>",
@@ -745,6 +746,7 @@ var Ls = {
"gou": 'naviger ett nivå opp">opp',
"gon": 'naviger til mappen etter denne">neste',
"logout": "Logg ut ",
+ "login": "Logg inn",
"access": " tilgang",
"ot_close": "lukk verktøy",
"ot_search": "søk etter filer ved å angi filnavn, mappenavn, tid, størrelse, eller metadata som sangtittel / artist / osv.$N$N<code>foo bar</code> = inneholder både «foo» og «bar»,$N<code>foo -bar</code> = inneholder «foo» men ikke «bar»,$N<code>^yana .opus$</code> = starter med «yana», filtype «opus»$N<code>"try unite"</code> = «try unite» eksakt$N$Ndatoformat er iso-8601, så f.eks.$N<code>2009-12-31</code> eller <code>2020-09-12 23:30:00</code>",
@@ -1375,6 +1377,7 @@ var Ls = {
"gou": '顶部">up',
"gon": '下一项">next',
"logout": " 登出",
+ "login": "登录", //m
"access": " 访问",
"ot_close": "关闭子菜单",
"ot_search": "按属性、路径/名称、音乐标签或上述内容的任意组合搜索文件$N$N<code>foo bar</code> = 必须包含 «foo» 和 «bar»,$N<code>foo -bar</code> = 包含 «foo» 而不包含 «bar»,$N<code>^yana .opus$</code> = 以 «yama» 为开头的 «opus» 文件$N<code>"try unite"</code> = 正好包含 «try unite»$N$N时间格式为 iso-8601, 比如:$N<code>2009-12-31</code> or <code>2020-09-12 23:30:00</code>",
@@ -2009,6 +2012,7 @@ var Ls = {
"gou": 'nadřazená složka">nahoru',
"gon": 'následující složka">následující',
"logout": "Odhlásit ",
+ "login": "Přihlásit se", //m
"access": " přístup",
"ot_close": "zavřít podnabídku",
"ot_search": "hledat soubory podle atributů, cesty / názvu, hudebních tagů nebo jejich kombinace$N$N<code>foo bar</code> = musí obsahovat jak «foo» tak «bar»,$N<code>foo -bar</code> = musí obsahovat «foo» ale ne «bar»,$N<code>^yana .opus$</code> = začíná na «yana» a je to «opus» soubor$N<code>"try unite"</code> = obsahuje přesně «try unite»$N$Nformát data je iso-8601, jako$N<code>2009-12-31</code> nebo <code>2020-09-12 23:30:00</code>",
@@ -2639,6 +2643,7 @@ var Ls = {
"gou": 'zum übergeordneter Ordner springen">hoch',
"gon": 'zum nächsten Ordner springen">nächst.',
"logout": "Abmelden ",
+ "login": "Anmelden", //m
"access": " Zugriff",
"ot_close": "Submenu schliessen",
"ot_search": "Dateien nach Attributen, Pfad/Name, Musiktags oder beliebiger Kombination suchen$N$N<code>foo bar</code> = muss «foo» und «bar» enthalten,$N<code>foo -bar</code> = muss «foo» aber nicht «bar» enthalten,$N<code>^yana .opus$</code> = beginnt mit «yana» und ist «opus»-Datei$N<code>"try unite"</code> = genau «try unite» enthalten$N$NDatumsformat ist iso-8601, z.B.$N<code>2009-12-31</code> oder <code>2020-09-12 23:30:00</code>",
@@ -3269,6 +3274,7 @@ var Ls = {
"gou": 'ylempi hakemisto">ylös',
"gon": 'seuraava hakemisto">seur',
"logout": "Kirjaudu ulos ",
+ "login": "Kirjaudu sisään", //m
"access": " -oikeudet",
"ot_close": "sulje alavalikko",
"ot_search": "etsi tiedostoja ominaisuuksien, tiedostopolun tai -nimen, musiikkitägien tai näiden yhdistelmän perusteella$N$N<code>foo bar</code> = täytyy sisältää sekä «foo» että «bar»,$N<code>foo -bar</code> = täytyy sisältää «foo» mutta ei «bar»,$N<code>^yana .opus$</code> = alkaa «yana» ja on «opus»-tiedosto$N<code>"try unite"</code> = sisältää täsmälleen «try unite»$N$Npäivämäärän muoto on iso-8601, kuten$N<code>2009-12-31</code> tai <code>2020-09-12 23:30:00</code>",
@@ -3899,6 +3905,7 @@ var Ls = {
"gou": 'dossier parent">haut',
"gon": 'dossier suivant">suivant',
"logout": "Déconnexion ",
+ "login": "Se connecter", //m
"access": " accès",
"ot_close": "fermer le sous-menu",
"ot_search": "chercher des fichiers par leurs attributs, chemin / nom, tag musicaux, ou nimporte quelle combinaison de ces options$N$N<code>foo bar</code> = doit contenir à la fois «foo» et «bar»,$N<code>foo -bar</code> = doit contenir «foo» mais pas «bar»,$N<code>^yana .opus$</code> = commence par «yana» et est un fichier «opus»$N<code>"try unite"</code> = contient exactement «try unite»$N$Nle format de date est iso-8601, comme$N<code>2009-12-31</code> ou <code>2020-09-12 23:30:00</code>",
@@ -4529,6 +4536,7 @@ var Ls = {
"gou": 'γονικός φάκελος">πάνω',
"gon": 'επόμενος φάκελος">επόμενο',
"logout": "Αποσύνδεση ",
+ "login": "Σύνδεση", //m
"access": " πρόσβαση",
"ot_close": "κλείσιμο υπομενού",
"ot_search": "αναζήτηση αρχείων με βάση χαρακτηριστικά, διαδρομή / όνομα, μουσικά tags ή οποιονδήποτε συνδυασμό$N$N<code>foo bar</code> = πρέπει να περιέχει και τα «foo» και «bar»,$N<code>foo -bar</code> = πρέπει να περιέχει το «foo» αλλά όχι το «bar»,$N<code>^yana .opus$</code> = να ξεκινά με «yana» και να είναι αρχείο «opus»$N<code>"try unite"</code> = να περιέχει ακριβώς «try unite»$N$Nη μορφή ημερομηνίας είναι iso-8601, όπως$N<code>2009-12-31</code> ή <code>2020-09-12 23:30:00</code>",
@@ -5159,6 +5167,7 @@ var Ls = {
"gou": 'cartella genitore">su',
"gon": 'prossima cartella">succ',
"logout": "Logout ",
+ "login": "Accedi", //m
"access": " accesso",
"ot_close": "chiudi sottomenu",
"ot_search": "cerca file per attributi, percorso / nome, tag musicali, o qualsiasi combinazione di questi$N$N<code>foo bar</code> = deve contenere sia «foo» che «bar»,$N<code>foo -bar</code> = deve contenere «foo» ma non «bar»,$N<code>^yana .opus$</code> = inizia con «yana» ed è un file «opus»$N<code>"try unite"</code> = contiene esattamente «try unite»$N$Nil formato data è iso-8601, come$N<code>2009-12-31</code> o <code>2020-09-12 23:30:00</code>",
@@ -5789,6 +5798,7 @@ var Ls = {
"gou": '상위 폴더">위로',
"gon": '다음 폴더">다음',
"logout": "로그아웃 ",
+ "login": "로그인", //m
"access": " 액세스",
"ot_close": "하위 메뉴 닫기",
"ot_search": "속성, 경로/이름, 음악 태그 또는 이들의 조합으로 파일을 검색합니다.$N$N<code>foo bar</code> = «foo»와 «bar»를 모두 포함해야 함,$N<code>foo -bar</code> = «foo»는 포함하지만 «bar»는 포함하지 않아야 함,$N<code>^yana .opus$</code> = «yana»로 시작하고 «opus» 파일이어야 함$N<code>"try unite"</code> = 정확히 «try unite»를 포함해야 함$N$N날짜 형식은 ISO-8601입니다. 예:$N<code>2009-12-31</code> 또는 <code>2020-09-12 23:30:00</code>",
@@ -6419,6 +6429,7 @@ var Ls = {
"gou": 'Bovenligende map">Omhoog',
"gon": 'Volgende map">Volgende',
"logout": "Uitloggen ",
+ "login": "Inloggen", //m
"access": " Toegang",
"ot_close": "Sluit onder-menu",
"ot_search": "Zoek voor bestanden bij attributes, pad / naam, muziek tags, of elk andere combinatie tussen$N$N<code>foo bar</code> = moet beide «foo» en «bar» bevatten,$N<code>foo -bar</code> = moet «foo» bevatten maar geen «bar»,$N<code>^yana .opus$</code> = start met «yana» en moet een «opus» bestand zijn$N<code>"try unite"</code> = moet precies «try unite» bevatten$N$Nde datum formaat is iso-8601, zoals$N<code>2009-12-31</code> of <code>2020-09-12 23:30:00</code>",
@@ -7050,6 +7061,7 @@ var Ls = {
"gou": 'navigér eitt nivå opp">opp',
"gon": 'navigér åt mappa etter den her">neste',
"logout": "Logg ut ",
+ "login": "Logg inn",
"access": " åtgang",
"ot_close": "lukk reiskap",
"ot_search": "søk etter filer ved å angje filnamn, mappenamn, tid, storleik, eller metadata som songtittel / artist / osv.$N$N<code>foo bar</code> = inneheld båe «foo» og «bar»,$N<code>foo -bar</code> = innehold «foo» men ikkje «bar»,$N<code>^yana .opus$</code> = startar med «yana», filtype «opus»$N<code>"try unite"</code> = «try unite» eksakt$N$Ndatoformat er iso-8601, så f.eks.$N<code>2009-12-31</code> eller <code>2020-09-12 23:30:00</code>",
@@ -7679,6 +7691,7 @@ var Ls = {
"gou": 'nadrzędny folder">w górę',
"gon": 'następny folder">następny',
"logout": "Wyloguj ",
+ "login": "Zaloguj się", //m
"access": " dostęp",
"ot_close": "zamknij pod-menu",
"ot_search": "szukaj plików po atrybutach, ścieżce / nazwie, tagach muzyki, bądź dowolnej ich kombinacji$N$N<code>foo bar</code> = musi zawierać «foo» oraz «bar»,$N<code>foo -bar</code> = musi zawierać «foo», lecz nie «bar»,$N<code>^yana .opus$</code> = musi zaczynać się od «yana» i być plikiem «opus»$N<code>"try unite"</code> = zawierać dokładnie «try unite»$N$Nformatem daty jest iso-8601, czyli$N<code>2009-12-31</code> lub <code>2020-09-12 23:30:00</code>",
@@ -8307,6 +8320,7 @@ var Ls = {
"gou": 'pasta pai">acima',
"gon": 'próxima pasta">próximo',
"logout": "Sair ",
+ "login": "Fazer login",
"access": " acesso",
"ot_close": "fechar submenu",
"ot_search": "procurar arquivos por atributos, caminho / nome, tags de música ou qualquer combinação deles$N$N<code>foo bar</code> = deve conter ambos «foo» e «bar»,$N<code>foo -bar</code> = deve conter «foo» mas não «bar»,$N<code>^yana .opus$</code> = começar com «yana» e ser um arquivo «opus»$N<code>"try unite"</code> = conter exatamente «try unite»$N$No formato de data é iso-8601, como$N<code>2009-12-31</code> ou <code>2020-09-12 23:30:00</code>",
@@ -8937,6 +8951,7 @@ var Ls = {
"gou": 'родительская папка">вверх',
"gon": 'следующая папка">след',
"logout": "Выйти ",
+ "login": "Войти", //m
"access": " доступ",
"ot_close": "закрыть подменю",
"ot_search": "искать файлы по атрибутам, пути / имени, музыкальным тегам или любой другой комбинации из следующих конструкций$N$N<code>foo bar</code> = обязано содержать «foo» И «bar»,$N<code>foo -bar</code> = обязано содержать «foo», но не «bar»,$N<code>^yana .opus$</code> = начинается с «yana» и имеет расширение «opus»$N<code>"try unite"</code> = содержит именно «try unite»$N$Nформат времени задаётся по стандарту iso-8601, например$N<code>2009-12-31</code> или <code>2020-09-12 23:30:00</code>",
@@ -9567,6 +9582,7 @@ var Ls = {
"gou": 'carpeta de nivel superior">subir',
"gon": 'siguiente carpeta">siguiente',
"logout": "Cerrar sesión ",
+ "login": "Iniciar sesión", //m
"access": " acceso",
"ot_close": "cerrar submenú",
"ot_search": "buscar archivos por atributos, ruta / nombre, etiquetas de música, o cualquier combinación$N$N<code>foo bar</code> = debe contener «foo» y «bar»,$N<code>foo -bar</code> = debe contener «foo» pero no «bar»,$N<code>^yana .opus$</code> = empieza con «yana» y es un archivo «opus»$N<code>"try unite"</code> = contiene exactamente «try unite»$N$Nel formato de fecha es iso-8601, como$N<code>2009-12-31</code> o <code>2020-09-12 23:30:00</code>",
@@ -10196,6 +10212,7 @@ var Ls = {
"gou": 'överordnad mapp">upp',
"gon": 'nästa mapp">nästa',
"logout": "Logga ut ",
+ "login": "Logga in", //m
"access": "-rättighet",
"ot_close": "stäng undermeny",
"ot_search": "sök efter filer via attribut, sökväg / namn, musiktaggar, eller någon kombination av dessa$N$N<code>foo bar</code> = måste innehålla både «foo» och «bar»,$N<code>foo -bar</code> = måste innehålla «foo» men inte «bar»,$N<code>^yana .opus$</code> = måste börja med «yana» och vara en «opus»-fil$N<code>"try unite"</code> = måste innehålla exakt «try unite»$N$Ndatumformatet är iso-8601, t.ex.$N<code>2009-12-31</code> eller <code>2020-09-12 23:30:00</code>",
@@ -10826,6 +10843,7 @@ var Ls = {
"gou": 'батьківська папка">вгору',
"gon": 'наступна папка">далі',
"logout": "Вийти ",
+ "login": "увійти", //m
"access": " доступ",
"ot_close": "закрити підменю",
"ot_search": "пошук файлів за атрибутами, шляхом / іменем, музичними тегами, або будь-якою комбінацією$N$N<code>foo bar</code> = має містити «foo» і «bar»,$N<code>foo -bar</code> = має містити «foo», але не «bar»,$N<code>^yana .opus$</code> = починатися з «yana» і бути файлом «opus»$N<code>"try unite"</code> = містити точно «try unite»$N$Nформат дати - iso-8601, наприклад$N<code>2009-12-31</code> або <code>2020-09-12 23:30:00</code>",
@@ -18289,10 +18307,14 @@ function apply_perms(res) {
axs += '-Only';
}
+ var dst = "?h";
+ if (idp_login && acct == "*")
+ dst = idp_login.replace(/\{dst\}/g, get_evpath());
+
ebi('acc_info').innerHTML = '' + srvinf +
'' + (acct != '*' ?
- '' :
- 'Login');
+ '' :
+ '' + L.login + '');
var o = QSA('#ops>a[data-perm]');
for (var a = 0; a < o.length; a++) {
diff --git a/copyparty/web/splash.css b/copyparty/web/splash.css
index 3b4d0f68..fb5d5075 100644
--- a/copyparty/web/splash.css
+++ b/copyparty/web/splash.css
@@ -38,6 +38,7 @@ a {
td a {
margin: 0;
}
+#wb,
#w {
color: #fff;
background: #940;
diff --git a/copyparty/web/splash.html b/copyparty/web/splash.html
index 61dd973d..1b9a89be 100644
--- a/copyparty/web/splash.html
+++ b/copyparty/web/splash.html
@@ -21,7 +21,11 @@
{%- if this.uname == '*' %}