mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-21 22:18:13 +02:00
L'acces distant suit la cle de pilotage courante
withLocalAuth capturait la cle UNE FOIS a l'ouverture du tunnel. Changer la cle ensuite, depuis l'interface ou avec set-web-key, faisait donc injecter l'ANCIENNE dans chaque requete venue du relais : tout l'acces distant passait en 401 jusqu'au prochain redemarrage du service. Et comme le chat n'affiche pas le code HTTP d'un flux qui n'arrive jamais, le symptome etait un « chargement de la conversation » infini, sans une ligne d'explication. La cle est relue a chaque requete. Cout nul : requireWebAuth le fait deja de l'autre cote. Deux tests, dont un qui echoue bien sur l'ancien code.
This commit is contained in:
1 parent
0ad961d75f
commit
59eb2357e9
2 files changed
+74
-2
No files matched your search
@@ -521,10 +521,18 @@ func newLinkHandler(mux *http.ServeMux) http.Handler {
|
|||||||
// par le tunnel. Le navigateur distant ne connaît que le token (vérifié par le
|
// par le tunnel. Le navigateur distant ne connaît que le token (vérifié par le
|
||||||
// relais) ; c'est ici, en local, qu'on satisfait l'auth de l'API web sans
|
// relais) ; c'est ici, en local, qu'on satisfait l'auth de l'API web sans
|
||||||
// exposer la clé au client.
|
// exposer la clé au client.
|
||||||
|
//
|
||||||
|
// ⚠️ La clé est relue À CHAQUE REQUÊTE, et surtout pas capturée une fois pour
|
||||||
|
// toutes à l'ouverture du tunnel. Avec une capture, changer la clé (depuis
|
||||||
|
// l'interface ou `ajean set-web-key`) faisait injecter l'ANCIENNE jusqu'au
|
||||||
|
// prochain redémarrage du service : tout l'accès distant tombait en 401, et
|
||||||
|
// comme le chat n'affiche pas le code HTTP d'un flux qui n'arrive jamais, le
|
||||||
|
// symptôme était un « chargement de la conversation » infini, sans la moindre
|
||||||
|
// erreur pour dire pourquoi. Le coût est nul : requireWebAuth relit déjà la clé
|
||||||
|
// à chaque requête de l'autre côté.
|
||||||
func withLocalAuth(next http.Handler) http.Handler {
|
func withLocalAuth(next http.Handler) http.Handler {
|
||||||
webKey := readWebKey()
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if webKey != "" && r.Header.Get("Authorization") == "" {
|
if webKey := readWebKey(); webKey != "" && r.Header.Get("Authorization") == "" {
|
||||||
r.Header.Set("Authorization", "Bearer "+webKey)
|
r.Header.Set("Authorization", "Bearer "+webKey)
|
||||||
}
|
}
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package ajean
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Changer la clé de pilotage ne doit PAS couper l'accès distant jusqu'au
|
||||||
|
// prochain redémarrage. La clé était capturée à l'ouverture du tunnel : après un
|
||||||
|
// `ajean set-web-key`, le tunnel injectait l'ancienne, tout passait en 401, et
|
||||||
|
// le portail restait bloqué sur « chargement de la conversation » sans jamais
|
||||||
|
// dire pourquoi.
|
||||||
|
func TestWithLocalAuthSuitLaCleCourante(t *testing.T) {
|
||||||
|
testHome(t)
|
||||||
|
if err := putStr(bkState, "web_key", "cle-numero-1"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Handler protégé, comme l'API web réelle.
|
||||||
|
protege := withLocalAuth(requireWebAuth(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(200)
|
||||||
|
}))
|
||||||
|
appel := func() int {
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
protege.ServeHTTP(rr, httptest.NewRequest("GET", "/api/status", nil))
|
||||||
|
return rr.Code
|
||||||
|
}
|
||||||
|
if code := appel(); code != 200 {
|
||||||
|
t.Fatalf("clé initiale : HTTP %d", code)
|
||||||
|
}
|
||||||
|
// La clé change pendant que le tunnel tourne (bouton de l'interface, CLI).
|
||||||
|
if err := putStr(bkState, "web_key", "cle-numero-2"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if code := appel(); code != 200 {
|
||||||
|
t.Fatalf("après changement de clé : HTTP %d — le tunnel injecte encore l'ancienne", code)
|
||||||
|
}
|
||||||
|
// Clé retirée : l'API est ouverte, le tunnel ne doit rien casser non plus.
|
||||||
|
if err := putStr(bkState, "web_key", ""); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if code := appel(); code != 200 {
|
||||||
|
t.Fatalf("sans clé : HTTP %d", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Une requête qui porte DÉJÀ une autorisation n'est pas réécrite : c'est le
|
||||||
|
// client distant qui décide dans ce cas.
|
||||||
|
func TestWithLocalAuthNecraseAucunEnTete(t *testing.T) {
|
||||||
|
testHome(t)
|
||||||
|
if err := putStr(bkState, "web_key", "cle-locale"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var vu string
|
||||||
|
protege := withLocalAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vu = r.Header.Get("Authorization")
|
||||||
|
}))
|
||||||
|
req := httptest.NewRequest("GET", "/api/status", nil)
|
||||||
|
req.Header.Set("Authorization", "Bearer venue-du-client")
|
||||||
|
protege.ServeHTTP(httptest.NewRecorder(), req)
|
||||||
|
if vu != "Bearer venue-du-client" {
|
||||||
|
t.Fatalf("en-tête client écrasé : %q", vu)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user