Skip to content

Commit 5bae597

Browse files
committed
feat(integrations): automatically disable googlereader/fever when not used
When there is no user of Fever/GoogleReader, there is no need to expose their endpoints. This reduces quite a bit the exposition surface of miniflux, while not breaking any existing deployments, and is pretty self-contained.
1 parent 86e2ce6 commit 5bae597

File tree

5 files changed

+43
-1
lines changed

5 files changed

+43
-1
lines changed

internal/fever/handler.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ func Serve(router *mux.Router, store *storage.Storage) {
2525
handler := &handler{store, router}
2626

2727
sr := router.PathPrefix("/fever").Subrouter()
28-
sr.Use(newMiddleware(store).serve)
28+
middleware := newMiddleware(store)
29+
sr.Use(middleware.maybeUnauthorizedFever)
30+
sr.Use(middleware.serve)
2931
sr.HandleFunc("/", handler.serve).Name("feverEndpoint")
3032
}
3133

internal/fever/middleware.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/http"
1010

1111
"miniflux.app/v2/internal/http/request"
12+
"miniflux.app/v2/internal/http/response"
1213
"miniflux.app/v2/internal/http/response/json"
1314
"miniflux.app/v2/internal/storage"
1415
)
@@ -21,6 +22,20 @@ func newMiddleware(s *storage.Storage) *middleware {
2122
return &middleware{s}
2223
}
2324

25+
func (m *middleware) maybeUnauthorizedFever(next http.Handler) http.Handler {
26+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
27+
if m.store.IsFeverUsed() {
28+
next.ServeHTTP(w, r)
29+
} else {
30+
builder := response.New(w, r)
31+
builder.WithStatus(http.StatusUnauthorized)
32+
builder.WithHeader("Content-Type", "text/plain")
33+
builder.WithBody("Unauthorized")
34+
builder.Write()
35+
}
36+
})
37+
}
38+
2439
func (m *middleware) serve(next http.Handler) http.Handler {
2540
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2641
clientIP := request.ClientIP(r)

internal/googlereader/handler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func Serve(router *mux.Router, store *storage.Storage) {
4949
sr := router.PathPrefix("/reader/api/0").Subrouter()
5050
sr.Use(middleware.handleCORS)
5151
sr.Use(middleware.apiKeyAuth)
52+
sr.Use(middleware.maybeUnauthorizedGoogleReader)
5253
sr.Methods(http.MethodOptions)
5354
sr.HandleFunc("/token", handler.tokenHandler).Methods(http.MethodGet).Name("Token")
5455
sr.HandleFunc("/edit-tag", handler.editTagHandler).Methods(http.MethodPost).Name("EditTag")

internal/googlereader/middleware.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ func newMiddleware(s *storage.Storage) *middleware {
2525
return &middleware{s}
2626
}
2727

28+
func (m *middleware) maybeUnauthorizedGoogleReader(next http.Handler) http.Handler {
29+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
30+
if m.store.IsGoogleReaderUsed() {
31+
next.ServeHTTP(w, r)
32+
} else {
33+
Unauthorized(w, r)
34+
}
35+
})
36+
}
37+
2838
func (m *middleware) handleCORS(next http.Handler) http.Handler {
2939
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3040
w.Header().Set("Access-Control-Allow-Origin", "*")

internal/storage/integration.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,13 @@ func (s *Storage) UserByFeverToken(token string) (*model.User, error) {
5252
}
5353
}
5454

55+
func (s *Storage) IsFeverUsed() bool {
56+
query := `SELECT true FROM integrations WHERE fever_enabled=true`
57+
var result bool
58+
s.db.QueryRow(query).Scan(&result)
59+
return result
60+
}
61+
5562
// GoogleReaderUserCheckPassword validates the Google Reader hashed password.
5663
func (s *Storage) GoogleReaderUserCheckPassword(username, password string) error {
5764
var hash string
@@ -105,6 +112,13 @@ func (s *Storage) GoogleReaderUserGetIntegration(username string) (*model.Integr
105112
return &integration, nil
106113
}
107114

115+
func (s *Storage) IsGoogleReaderUsed() bool {
116+
query := `SELECT true FROM integrations WHERE googlereader_enabled=true`
117+
var result bool
118+
s.db.QueryRow(query).Scan(&result)
119+
return result
120+
}
121+
108122
// Integration returns user integration settings.
109123
func (s *Storage) Integration(userID int64) (*model.Integration, error) {
110124
query := `

0 commit comments

Comments
 (0)