diff --git a/publish.go b/publish.go index f1c38c38..bf0c327d 100644 --- a/publish.go +++ b/publish.go @@ -8,6 +8,7 @@ import ( "go.uber.org/zap" ) +//nolint:funlen // PublishHandler allows publisher to broadcast updates to all subscribers. func (h *Hub) PublishHandler(w http.ResponseWriter, r *http.Request) { var claims *claims @@ -47,10 +48,21 @@ func (h *Hub) PublishHandler(w http.ResponseWriter, r *http.Request) { } private := len(r.PostForm["private"]) != 0 - if private && !canDispatch(h.topicSelectorStore, topics, claims.Mercure.Publish) { - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + if !canDispatch(h.topicSelectorStore, topics, claims.Mercure.Publish) { + if private { + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - return + return + } + + if h.isBackwardCompatiblyEnabledWith(7) { + h.logger.Info("Deprecated: posting public updates to topics not listed in the 'mercure.publish' JWT claim is deprecated since the version 7 of the protocol, use '[\"*\"]' as value to allow publishing on all topics.") + } else { + h.logger.Info("Unsupported: posting public updates to topics not listed in the 'mercure.publish' JWT claim is not supported anymore, use '[\"*\"]' as value to allow publishing on all topics or enable backward compatibility with the version 7 of the protocol.") + http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + + return + } } u := &Update{ diff --git a/publish_test.go b/publish_test.go index 1fef9dfd..a6e3bf78 100644 --- a/publish_test.go +++ b/publish_test.go @@ -15,7 +15,7 @@ import ( "go.uber.org/zap" ) -func TestNoAuthorizationHeader(t *testing.T) { +func TestPublishNoAuthorizationHeader(t *testing.T) { hub := createDummy() req := httptest.NewRequest("POST", defaultHubURL, nil) @@ -63,7 +63,7 @@ func TestPublishBadContentType(t *testing.T) { hub := createDummy() req := httptest.NewRequest("POST", defaultHubURL, nil) - req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(hub, rolePublisher, []string{})) + req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(hub, rolePublisher, []string{"*"})) req.Header.Add("Content-Type", "text/plain; boundary=") w := httptest.NewRecorder() hub.PublishHandler(w, req) @@ -78,7 +78,7 @@ func TestPublishNoTopic(t *testing.T) { hub := createDummy() req := httptest.NewRequest("POST", defaultHubURL, nil) - req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(hub, rolePublisher, []string{})) + req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(hub, rolePublisher, []string{"*"})) w := httptest.NewRecorder() hub.PublishHandler(w, req) @@ -89,11 +89,13 @@ func TestPublishNoTopic(t *testing.T) { assert.Equal(t, "Missing \"topic\" parameter\n", w.Body.String()) } -func TestPublishNoData(t *testing.T) { +func TestPublishInvalidRetry(t *testing.T) { hub := createDummy() form := url.Values{} form.Add("topic", "http://example.com/books/1") + form.Add("data", "foo") + form.Add("retry", "invalid") req := httptest.NewRequest("POST", defaultHubURL, strings.NewReader(form.Encode())) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") @@ -105,20 +107,21 @@ func TestPublishNoData(t *testing.T) { resp := w.Result() defer resp.Body.Close() - assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, http.StatusBadRequest, resp.StatusCode) + assert.Equal(t, "Invalid \"retry\" parameter\n", w.Body.String()) } -func TestPublishInvalidRetry(t *testing.T) { +func TestPublishNotAuthorizedTopicSelector(t *testing.T) { hub := createDummy() form := url.Values{} form.Add("topic", "http://example.com/books/1") form.Add("data", "foo") - form.Add("retry", "invalid") + form.Add("private", "on") req := httptest.NewRequest("POST", defaultHubURL, strings.NewReader(form.Encode())) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(hub, rolePublisher, []string{})) + req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(hub, rolePublisher, []string{"foo"})) w := httptest.NewRecorder() hub.PublishHandler(w, req) @@ -126,21 +129,18 @@ func TestPublishInvalidRetry(t *testing.T) { resp := w.Result() defer resp.Body.Close() - assert.Equal(t, http.StatusBadRequest, resp.StatusCode) - assert.Equal(t, "Invalid \"retry\" parameter\n", w.Body.String()) + assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) } -func TestPublishNotAuthorizedTopicSelector(t *testing.T) { +func TestPublishEmptyTopicSelector(t *testing.T) { hub := createDummy() form := url.Values{} form.Add("topic", "http://example.com/books/1") - form.Add("data", "foo") - form.Add("private", "on") req := httptest.NewRequest("POST", defaultHubURL, strings.NewReader(form.Encode())) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(hub, rolePublisher, []string{"foo"})) + req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(hub, rolePublisher, []string{})) w := httptest.NewRecorder() hub.PublishHandler(w, req) @@ -151,6 +151,25 @@ func TestPublishNotAuthorizedTopicSelector(t *testing.T) { assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) } +func TestPublishLegacyAuthorization(t *testing.T) { + hub := createDummy(WithProtocolVersionCompatibility(7)) + + form := url.Values{} + form.Add("topic", "http://example.com/books/1") + + req := httptest.NewRequest("POST", defaultHubURL, strings.NewReader(form.Encode())) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(hub, rolePublisher, []string{})) + + w := httptest.NewRecorder() + hub.PublishHandler(w, req) + + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusOK, resp.StatusCode) +} + func TestPublishOK(t *testing.T) { hub := createDummy() @@ -197,6 +216,25 @@ func TestPublishOK(t *testing.T) { wg.Wait() } +func TestPublishNoData(t *testing.T) { + hub := createDummy() + + form := url.Values{} + form.Add("topic", "http://example.com/books/1") + + req := httptest.NewRequest("POST", defaultHubURL, strings.NewReader(form.Encode())) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(hub, rolePublisher, []string{"*"})) + + w := httptest.NewRecorder() + hub.PublishHandler(w, req) + + resp := w.Result() + defer resp.Body.Close() + + assert.Equal(t, http.StatusOK, resp.StatusCode) +} + func TestPublishGenerateUUID(t *testing.T) { h := createDummy() @@ -222,7 +260,7 @@ func TestPublishGenerateUUID(t *testing.T) { req := httptest.NewRequest("POST", defaultHubURL, strings.NewReader(form.Encode())) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(h, rolePublisher, []string{})) + req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(h, rolePublisher, []string{"*"})) w := httptest.NewRecorder() h.PublishHandler(w, req) diff --git a/server_test.go b/server_test.go index 2879efbe..fac96ef4 100644 --- a/server_test.go +++ b/server_test.go @@ -47,7 +47,7 @@ func TestForwardedHeaders(t *testing.T) { req, _ := http.NewRequest("POST", testURL, strings.NewReader(body.Encode())) req.Header.Add("X-Forwarded-For", "192.0.2.1") req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(h, rolePublisher, []string{})) + req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(h, rolePublisher, []string{"*"})) resp2, err := client.Do(req) require.Nil(t, err) @@ -209,7 +209,7 @@ func TestServe(t *testing.T) { body := url.Values{"topic": {"http://example.com/foo/1", "http://example.com/alt/1"}, "data": {"hello"}, "id": {"first"}} req, _ := http.NewRequest("POST", testURL, strings.NewReader(body.Encode())) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(h, rolePublisher, []string{})) + req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(h, rolePublisher, []string{"*"})) resp2, err := client.Do(req) require.Nil(t, err) @@ -279,7 +279,7 @@ func TestClientClosesThenReconnects(t *testing.T) { req, err := http.NewRequest("POST", testURL, strings.NewReader(body.Encode())) require.Nil(t, err) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(h, rolePublisher, []string{})) + req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(h, rolePublisher, []string{"*"})) resp, err := client.Do(req) require.Nil(t, err) @@ -469,7 +469,7 @@ func (s *testServer) newSubscriber(topic string, keepAlive bool) { func (s *testServer) publish(body url.Values) { req, _ := http.NewRequest("POST", testURL, strings.NewReader(body.Encode())) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(s.h, rolePublisher, []string{})) + req.Header.Add("Authorization", "Bearer "+createDummyAuthorizedJWT(s.h, rolePublisher, []string{"*"})) resp, err := s.client.Do(req) require.Nil(s.t, err) diff --git a/subscribe.go b/subscribe.go index 1573ec10..44968aaf 100644 --- a/subscribe.go +++ b/subscribe.go @@ -165,13 +165,13 @@ func retrieveLastEventID(r *http.Request, opt *opt, logger Logger) string { if legacyEventIDValues, present := query["Last-Event-ID"]; present { if opt.isBackwardCompatiblyEnabledWith(7) { - logger.Info("deprecation: the 'Last-Event-ID' query parameter is deprecated since the version 8 of the protocol, use 'lastEventID' instead.") + logger.Info("Deprecated: the 'Last-Event-ID' query parameter is deprecated since the version 8 of the protocol, use 'lastEventID' instead.") if len(legacyEventIDValues) != 0 { return legacyEventIDValues[0] } } else { - logger.Info("unsupported: the 'Last-Event-ID' query parameter is not supported anymore, use 'lastEventID' instead or enable backward compatibility with version 7 of the protocol.") + logger.Info("Unsupported: the 'Last-Event-ID' query parameter is not supported anymore, use 'lastEventID' instead or enable backward compatibility with version 7 of the protocol.") } }