mirror of
https://github.com/juanfont/headscale.git
synced 2026-09-18 22:34:54 +09:00
hscontrol: read Apple platform via chi.URLParam, not mux.Vars
ApplePlatformConfig still used gorilla mux.Vars after the chi
migration in 30338441, so every /apple/{platform} request returned
400 "no platform specified". Read the param via chi and add tests.
Fixes #3296
This commit is contained in:
committed by
Kristoffer Dalby
parent
6777a82ee6
commit
b892b8f254
@@ -7,8 +7,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
textTemplate "text/template"
|
textTemplate "text/template"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/gofrs/uuid/v5"
|
"github.com/gofrs/uuid/v5"
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/juanfont/headscale/hscontrol/templates"
|
"github.com/juanfont/headscale/hscontrol/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,10 +36,8 @@ func (h *Headscale) ApplePlatformConfig(
|
|||||||
writer http.ResponseWriter,
|
writer http.ResponseWriter,
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
) {
|
) {
|
||||||
vars := mux.Vars(req)
|
platform := chi.URLParam(req, "platform")
|
||||||
|
if platform == "" {
|
||||||
platform, ok := vars["platform"]
|
|
||||||
if !ok {
|
|
||||||
httpError(writer, NewHTTPError(http.StatusBadRequest, "no platform specified", nil))
|
httpError(writer, NewHTTPError(http.StatusBadRequest, "no platform specified", nil))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package hscontrol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/juanfont/headscale/hscontrol/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestApplePlatformConfig_ServesProfilesViaChiRouter is the regression
|
||||||
|
// guard for issue juanfont/headscale#3296.
|
||||||
|
//
|
||||||
|
// The Apple profile download endpoints (`/apple/macos-app-store`,
|
||||||
|
// `/apple/macos-standalone`, `/apple/ios`) are registered on the chi
|
||||||
|
// router (see hscontrol/app.go: `r.Get("/apple/{platform}", ...)`).
|
||||||
|
// Before the fix, `ApplePlatformConfig` extracted the `{platform}` URL
|
||||||
|
// parameter via `mux.Vars(req)` from gorilla/mux; because the request
|
||||||
|
// never passed through a gorilla router, the lookup always missed and
|
||||||
|
// every download returned HTTP 400 `no platform specified`.
|
||||||
|
//
|
||||||
|
// This test mounts the route on a chi router exactly as production
|
||||||
|
// does so the assertion exercises the real router + handler wiring,
|
||||||
|
// not a hand-crafted chi context. It fails if the handler ever again
|
||||||
|
// reads URL parameters via an API the production router does not
|
||||||
|
// populate.
|
||||||
|
func TestApplePlatformConfig_ServesProfilesViaChiRouter(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
h := &Headscale{
|
||||||
|
cfg: &types.Config{
|
||||||
|
ServerURL: "https://headscale.example.com",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mirror the production mount in hscontrol/app.go so this test
|
||||||
|
// covers the actual router + handler wiring, not a hand-crafted
|
||||||
|
// chi context.
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Get("/apple/{platform}", h.ApplePlatformConfig)
|
||||||
|
|
||||||
|
srv := httptest.NewServer(r)
|
||||||
|
t.Cleanup(srv.Close)
|
||||||
|
|
||||||
|
platforms := []string{"macos-app-store", "macos-standalone", "ios"}
|
||||||
|
for _, platform := range platforms {
|
||||||
|
t.Run(platform, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
//nolint:noctx // test fixture
|
||||||
|
resp, err := http.Get(srv.URL + "/apple/" + platform)
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() { resp.Body.Close() })
|
||||||
|
|
||||||
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||||
|
body := string(bodyBytes)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, resp.StatusCode,
|
||||||
|
"expected 200 for /apple/%s, got %d: %s",
|
||||||
|
platform, resp.StatusCode, body)
|
||||||
|
assert.Equal(t,
|
||||||
|
"application/x-apple-aspen-config; charset=utf-8",
|
||||||
|
resp.Header.Get("Content-Type"),
|
||||||
|
"profile must be served as an Apple aspen config")
|
||||||
|
assert.Contains(t, body,
|
||||||
|
"https://headscale.example.com",
|
||||||
|
"rendered profile must embed the configured ServerURL")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestApplePlatformConfig_RejectsUnknownPlatform locks the contract for
|
||||||
|
// the `default:` branch of `ApplePlatformConfig`: an otherwise-valid
|
||||||
|
// request whose `{platform}` segment is none of the three known values
|
||||||
|
// must return HTTP 400 with the documented message. This catches both
|
||||||
|
// silent fallthrough (e.g. a future template registered under a new
|
||||||
|
// name without adding a case) and accidental message drift.
|
||||||
|
func TestApplePlatformConfig_RejectsUnknownPlatform(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
h := &Headscale{
|
||||||
|
cfg: &types.Config{
|
||||||
|
ServerURL: "https://headscale.example.com",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Get("/apple/{platform}", h.ApplePlatformConfig)
|
||||||
|
|
||||||
|
srv := httptest.NewServer(r)
|
||||||
|
t.Cleanup(srv.Close)
|
||||||
|
|
||||||
|
//nolint:noctx // test fixture
|
||||||
|
resp, err := http.Get(srv.URL + "/apple/windows-phone")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() { resp.Body.Close() })
|
||||||
|
|
||||||
|
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||||
|
body := string(bodyBytes)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode,
|
||||||
|
"unknown platform must be rejected with 400")
|
||||||
|
assert.Contains(t, body,
|
||||||
|
"platform must be ios, macos-app-store or macos-standalone",
|
||||||
|
"error body must list the supported platforms")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user