diff options
Diffstat (limited to 'server/internal')
| -rw-r--r-- | server/internal/api/api.go | 10 | ||||
| -rw-r--r-- | server/internal/api/api_test.go | 31 |
2 files changed, 37 insertions, 4 deletions
diff --git a/server/internal/api/api.go b/server/internal/api/api.go index 5a11c92..c974ecb 100644 --- a/server/internal/api/api.go +++ b/server/internal/api/api.go @@ -254,8 +254,14 @@ func (s *Server) episode(w http.ResponseWriter, r *http.Request) { }) } writeJSON(w, http.StatusOK, episodeResponse{ - Status: "ready", - AudioURL: "/audio/" + found.ID + "?token=" + url.QueryEscape(s.opts.Token), + Status: "ready", + // No token. Apache logs the request line, so one in the query put a copy of the secret + // in every line of a file logrotate keeps fourteen of. The phone sends + // Authorization: Bearer for this host -- the downloader always did, and the player does + // too since it got a ResolvingDataSource -- and tokenOK takes either, so the query + // parameter cost a leak and bought nothing. Query tokens are still accepted, because + // withdrawing that as well would lock out an older build with no way back in. + AudioURL: "/audio/" + found.ID, DurationMs: found.DurationMs, IntroMs: found.IntroMs, OutroMs: found.OutroMs, diff --git a/server/internal/api/api_test.go b/server/internal/api/api_test.go index 5ae4435..79ee904 100644 --- a/server/internal/api/api_test.go +++ b/server/internal/api/api_test.go @@ -257,8 +257,27 @@ func TestEpisodeResponseCarriesTheKindOfEachBreak(t *testing.T) { } } -// The audio URL has to carry the token, or the phone gets a link it cannot use. -func TestTheAudioUrlIsUsableAsGiven(t *testing.T) { +// The audio URL carries no token. Apache logs the request line, so a token in the query was a +// copy of the secret in every line of a file logrotate keeps fourteen of. The phone sends +// Authorization: Bearer for this host now -- the downloader always did, and the player does +// since the ResolvingDataSource went in -- so the query parameter bought nothing. +func TestTheAudioUrlCarriesNoToken(t *testing.T) { + s, st, _, audioDir := newServer(t) + seedMirrored(t, st, audioDir, "audio-bytes") + + rec := get(t, s, "/api/episode?feed=https://f&guid=g1") + var got episodeResponse + if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil { + t.Fatal(err) + } + + if strings.Contains(got.AudioURL, "token=") { + t.Errorf("url = %q, want no token in it", got.AudioURL) + } +} + +// Usable as given, given the header the phone already sends. +func TestTheAudioUrlIsUsableWithTheHeader(t *testing.T) { s, st, _, audioDir := newServer(t) seedMirrored(t, st, audioDir, "audio-bytes") @@ -269,6 +288,7 @@ func TestTheAudioUrlIsUsableAsGiven(t *testing.T) { } req := httptest.NewRequest(http.MethodGet, got.AudioURL, nil) + req.Header.Set("Authorization", "Bearer "+token) rec2 := httptest.NewRecorder() s.Handler().ServeHTTP(rec2, req) if rec2.Code != http.StatusOK { @@ -277,6 +297,13 @@ func TestTheAudioUrlIsUsableAsGiven(t *testing.T) { if rec2.Body.String() != "audio-bytes" { t.Errorf("body %q", rec2.Body.String()) } + + bare := httptest.NewRequest(http.MethodGet, got.AudioURL, nil) + rec3 := httptest.NewRecorder() + s.Handler().ServeHTTP(rec3, bare) + if rec3.Code != http.StatusUnauthorized { + t.Errorf("unauthenticated fetch returned %d, want 401", rec3.Code) + } } // Seeking is the normal case for a podcast, so byte ranges have to work. |
