summaryrefslogtreecommitdiff
path: root/server/internal
diff options
context:
space:
mode:
Diffstat (limited to 'server/internal')
-rw-r--r--server/internal/api/api.go10
-rw-r--r--server/internal/api/api_test.go31
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.