mirror of
https://github.com/karust/openserp.git
synced 2026-08-05 16:53:54 +08:00
94 lines
2.5 KiB
Go
94 lines
2.5 KiB
Go
//go:build integration
|
|
// +build integration
|
|
|
|
package core
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/karust/openserp/testutil"
|
|
)
|
|
|
|
func TestProxyPerContextAuthIsolationSpike(t *testing.T) {
|
|
testutil.RequireIntegration(t)
|
|
|
|
type authHit struct {
|
|
proxy string
|
|
auth string
|
|
}
|
|
var (
|
|
mu sync.Mutex
|
|
hits []authHit
|
|
)
|
|
|
|
newAuthProxy := func(name, username, password string) *httptest.Server {
|
|
want := "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
|
|
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
got := r.Header.Get("Proxy-Authorization")
|
|
mu.Lock()
|
|
hits = append(hits, authHit{proxy: name, auth: got})
|
|
mu.Unlock()
|
|
if got != want {
|
|
w.Header().Set("Proxy-Authenticate", `Basic realm="openserp-spike"`)
|
|
w.WriteHeader(http.StatusProxyAuthRequired)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
_, _ = fmt.Fprintf(w, "<html><body>%s</body></html>", name)
|
|
}))
|
|
}
|
|
|
|
proxyA := newAuthProxy("proxy-a", "user-a", "pass-a")
|
|
defer proxyA.Close()
|
|
proxyB := newAuthProxy("proxy-b", "user-b", "pass-b")
|
|
defer proxyB.Close()
|
|
|
|
browser, err := NewBrowser(BrowserOpts{IsHeadless: true, Timeout: 20 * time.Second})
|
|
if err != nil {
|
|
t.Fatalf("create browser: %v", err)
|
|
}
|
|
defer closeTestBrowser(t, browser)
|
|
|
|
run := func(proxyURL string) error {
|
|
ctx := WithRequestProxyURL(context.Background(), proxyURL)
|
|
page, err := browser.Navigate(ctx, "http://proxy-auth-spike.invalid/")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
_ = ClosePageWithTimeout(context.Background(), page, time.Second)
|
|
}()
|
|
body, err := page.Timeout(5 * time.Second).Element("body")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = body.Text()
|
|
return err
|
|
}
|
|
|
|
errCh := make(chan error, 2)
|
|
go func() { errCh <- run(strings.Replace(proxyA.URL, "http://", "http://user-a:pass-a@", 1)) }()
|
|
go func() { errCh <- run(strings.Replace(proxyB.URL, "http://", "http://user-b:pass-b@", 1)) }()
|
|
|
|
for i := 0; i < 2; i++ {
|
|
if err := <-errCh; err != nil {
|
|
t.Fatalf("proxied navigation failed: %v", err)
|
|
}
|
|
}
|
|
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
t.Logf("per-context auth spike report: %d proxy requests observed; Browser serializes authenticated proxy auth handlers to avoid cross-context credential leakage", len(hits))
|
|
for _, hit := range hits {
|
|
t.Logf("proxy=%s auth_prefix=%t", hit.proxy, strings.HasPrefix(hit.auth, "Basic "))
|
|
}
|
|
}
|