| 1 | package cli |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "net/http" |
| 6 | "net/http/httptest" |
| 7 | "strings" |
| 8 | "testing" |
| 9 | |
| 10 | "reasonix/internal/config" |
| 11 | "reasonix/internal/control" |
| 12 | "reasonix/internal/serve" |
| 13 | ) |
| 14 | |
| 15 | func TestRemoteServeBrowserURLUsesFragmentForCurrentServe(t *testing.T) { |
| 16 | ctrl := newOwnedTestController(t, control.Options{SessionDir: t.TempDir()}) |
| 17 | t.Cleanup(ctrl.Close) |
| 18 | srv := serve.New(ctrl, serve.NewBroadcaster(), config.ServeConfig{AuthMode: "token", Token: "current secret/+"}) |
| 19 | ts := httptest.NewServer(srv.Handler()) |
| 20 | t.Cleanup(ts.Close) |
| 21 | |
| 22 | bound := strings.TrimPrefix(ts.URL, "http://") |
| 23 | got := remoteServeBrowserURL(context.Background(), bound, "current secret/+") |
| 24 | want := ts.URL + "/#token=current+secret%2F%2B" |
| 25 | if got != want { |
| 26 | t.Fatalf("remoteServeBrowserURL() = %q, want %q", got, want) |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | func TestRemoteServeBrowserURLFallsBackForReusedV1214ServeContract(t *testing.T) { |
| 31 | const token = "legacy secret/+" |
| 32 | // v1.21.4 token auth denies unauthenticated /auth/token requests and |
| 33 | // bootstraps browser cookies only from the legacy query parameter. |
| 34 | legacy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 35 | if r.URL.Query().Get("token") != token { |
| 36 | http.Error(w, "Unauthorized", http.StatusUnauthorized) |
| 37 | return |
| 38 | } |
| 39 | http.SetCookie(w, &http.Cookie{Name: "reasonix_token", Value: token, Path: "/", HttpOnly: true}) |
| 40 | http.Redirect(w, r, "/", http.StatusFound) |
| 41 | })) |
| 42 | t.Cleanup(legacy.Close) |
| 43 | |
| 44 | bound := strings.TrimPrefix(legacy.URL, "http://") |
| 45 | got := remoteServeBrowserURL(context.Background(), bound, token) |
| 46 | want := legacy.URL + "/?token=legacy+secret%2F%2B" |
| 47 | if got != want { |
| 48 | t.Fatalf("remoteServeBrowserURL() = %q, want legacy query bootstrap %q", got, want) |
| 49 | } |
| 50 | |
| 51 | client := &http.Client{CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }} |
| 52 | resp, err := client.Get(got) |
| 53 | if err != nil { |
| 54 | t.Fatal(err) |
| 55 | } |
| 56 | defer resp.Body.Close() |
| 57 | if resp.StatusCode != http.StatusFound { |
| 58 | t.Fatalf("legacy bootstrap status = %d, want %d", resp.StatusCode, http.StatusFound) |
| 59 | } |
| 60 | cookies := resp.Cookies() |
| 61 | if len(cookies) != 1 || cookies[0].Name != "reasonix_token" || !cookies[0].HttpOnly { |
| 62 | t.Fatalf("legacy bootstrap cookies = %#v, want HttpOnly reasonix_token", cookies) |
| 63 | } |
| 64 | } |
| 65 |