check / check (push) Waiting to run
The auth cookie is always Secure, and Go 1.24's cookie jar sends a Secure cookie only over HTTPS, so neoirc-cli lost its session against a plain-HTTP server on localhost. Its jar now treats a server on localhost as HTTPS, as curl does. The README's Transport Security section and Python example say what a client needs. A test checks that closing a registered IRC client's connection stops its relay goroutine. The README Entrypoints section names a make target only for the scripts that have one, and the Dockerfile says why the test phase runs with -p 4. Model: opus-5-5
98 lines
2.0 KiB
Go
98 lines
2.0 KiB
Go
package neoircapi_test
|
|
|
|
import (
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
|
|
api "sneak.berlin/go/neoirc/internal/cli/api"
|
|
)
|
|
|
|
const cookieValue = "opaque-value"
|
|
|
|
// newSessionServer starts a plain-HTTP server that, like
|
|
// neoircd, sets a Secure auth cookie when a session is
|
|
// created and answers GET /api/v1/state only when that
|
|
// cookie comes back.
|
|
func newSessionServer(t *testing.T) *httptest.Server {
|
|
t.Helper()
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
mux.HandleFunc("GET /api/v1/server", func(
|
|
writer http.ResponseWriter, _ *http.Request,
|
|
) {
|
|
_, _ = io.WriteString(writer, `{}`)
|
|
})
|
|
|
|
mux.HandleFunc("POST /api/v1/session", func(
|
|
writer http.ResponseWriter, _ *http.Request,
|
|
) {
|
|
http.SetCookie(writer, &http.Cookie{
|
|
Name: "neoirc_auth",
|
|
Value: cookieValue,
|
|
Path: "/",
|
|
HttpOnly: true,
|
|
Secure: true,
|
|
SameSite: http.SameSiteStrictMode,
|
|
})
|
|
writer.WriteHeader(http.StatusCreated)
|
|
|
|
_, _ = io.WriteString(writer, `{"id":1,"nick":"alice"}`)
|
|
})
|
|
|
|
mux.HandleFunc("GET /api/v1/state", func(
|
|
writer http.ResponseWriter, request *http.Request,
|
|
) {
|
|
cookie, err := request.Cookie("neoirc_auth")
|
|
if err != nil || cookie.Value != cookieValue {
|
|
writer.WriteHeader(http.StatusUnauthorized)
|
|
|
|
return
|
|
}
|
|
|
|
_, _ = io.WriteString(
|
|
writer, `{"id":1,"nick":"alice","channels":[]}`,
|
|
)
|
|
})
|
|
|
|
server := httptest.NewServer(mux)
|
|
t.Cleanup(server.Close)
|
|
|
|
return server
|
|
}
|
|
|
|
func TestClientKeepsSessionOverPlainHTTPOnLocalhost(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
server := newSessionServer(t)
|
|
|
|
serverURL, err := url.Parse(server.URL)
|
|
if err != nil {
|
|
t.Fatalf("parse server URL: %v", err)
|
|
}
|
|
|
|
for _, host := range []string{"127.0.0.1", "localhost"} {
|
|
t.Run(host, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
client := api.NewClient(
|
|
"http://" + net.JoinHostPort(host, serverURL.Port()),
|
|
)
|
|
|
|
_, err := client.CreateSession("alice")
|
|
if err != nil {
|
|
t.Fatalf("create session: %v", err)
|
|
}
|
|
|
|
_, err = client.GetState()
|
|
if err != nil {
|
|
t.Fatalf("state after creating the session: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|