diff options
author | Frédéric Guillot <fred@miniflux.net> | 2018-04-27 20:38:46 -0700 |
---|---|---|
committer | Frédéric Guillot <fred@miniflux.net> | 2018-04-27 20:38:46 -0700 |
commit | 6b360d08c1f6c8a6cd1b7608f7af734a3ceef8d7 (patch) | |
tree | 48352d35fa9f3559df05accf4ce4fce1672a2830 /http | |
parent | 322b265d7aec7731f7fa703c9a74ceb61ae73f3f (diff) |
Use Gorilla middleware (refactoring)
Diffstat (limited to 'http')
-rw-r--r-- | http/handler/context.go | 2 | ||||
-rw-r--r-- | http/handler/handler.go | 14 | ||||
-rw-r--r-- | http/middleware/basic_auth.go | 72 | ||||
-rw-r--r-- | http/middleware/context_keys.go | 49 | ||||
-rw-r--r-- | http/middleware/fever.go | 57 | ||||
-rw-r--r-- | http/middleware/middleware.go | 36 | ||||
-rw-r--r-- | http/middleware/session.go | 85 | ||||
-rw-r--r-- | http/middleware/user_session.go | 86 |
8 files changed, 5 insertions, 396 deletions
diff --git a/http/handler/context.go b/http/handler/context.go index 35eb1ea..119a4d5 100644 --- a/http/handler/context.go +++ b/http/handler/context.go @@ -8,10 +8,10 @@ import ( "net/http" "github.com/miniflux/miniflux/crypto" - "github.com/miniflux/miniflux/http/middleware" "github.com/miniflux/miniflux/http/route" "github.com/miniflux/miniflux/locale" "github.com/miniflux/miniflux/logger" + "github.com/miniflux/miniflux/middleware" "github.com/miniflux/miniflux/model" "github.com/miniflux/miniflux/storage" diff --git a/http/handler/handler.go b/http/handler/handler.go index d698b2e..b88a885 100644 --- a/http/handler/handler.go +++ b/http/handler/handler.go @@ -9,35 +9,30 @@ import ( "time" "github.com/miniflux/miniflux/config" - "github.com/miniflux/miniflux/http/middleware" "github.com/miniflux/miniflux/locale" - "github.com/miniflux/miniflux/logger" "github.com/miniflux/miniflux/storage" "github.com/miniflux/miniflux/template" "github.com/miniflux/miniflux/timer" "github.com/gorilla/mux" - "github.com/tomasen/realip" ) // ControllerFunc is an application HTTP handler. type ControllerFunc func(ctx *Context, request *Request, response *Response) -// Handler manages HTTP handlers and middlewares. +// Handler manages HTTP handlers. type Handler struct { cfg *config.Config store *storage.Storage translator *locale.Translator template *template.Engine router *mux.Router - middleware *middleware.Chain } // Use is a wrapper around an HTTP handler. func (h *Handler) Use(f ControllerFunc) http.Handler { - return h.middleware.WrapFunc(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer timer.ExecutionTime(time.Now(), r.URL.Path) - logger.Debug("[HTTP] %s %s %s", realip.RealIP(r), r.Method, r.URL.Path) if r.Header.Get("X-Forwarded-Proto") == "https" { h.cfg.IsHTTPS = true @@ -55,17 +50,16 @@ func (h *Handler) Use(f ControllerFunc) http.Handler { } f(ctx, request, response) - })) + }) } // NewHandler returns a new Handler. -func NewHandler(cfg *config.Config, store *storage.Storage, router *mux.Router, template *template.Engine, translator *locale.Translator, middleware *middleware.Chain) *Handler { +func NewHandler(cfg *config.Config, store *storage.Storage, router *mux.Router, template *template.Engine, translator *locale.Translator) *Handler { return &Handler{ cfg: cfg, store: store, translator: translator, router: router, template: template, - middleware: middleware, } } diff --git a/http/middleware/basic_auth.go b/http/middleware/basic_auth.go deleted file mode 100644 index 35a9f81..0000000 --- a/http/middleware/basic_auth.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2017 Frédéric Guillot. All rights reserved. -// Use of this source code is governed by the Apache 2.0 -// license that can be found in the LICENSE file. - -package middleware - -import ( - "context" - "net/http" - - "github.com/miniflux/miniflux/logger" - "github.com/miniflux/miniflux/storage" -) - -// BasicAuthMiddleware is the middleware for HTTP Basic authentication. -type BasicAuthMiddleware struct { - store *storage.Storage -} - -// Handler executes the middleware. -func (b *BasicAuthMiddleware) Handler(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) - errorResponse := `{"error_message": "Not Authorized"}` - - username, password, authOK := r.BasicAuth() - if !authOK { - logger.Debug("[Middleware:BasicAuth] No authentication headers sent") - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(errorResponse)) - return - } - - if err := b.store.CheckPassword(username, password); err != nil { - logger.Info("[Middleware:BasicAuth] Invalid username or password: %s", username) - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(errorResponse)) - return - } - - user, err := b.store.UserByUsername(username) - if err != nil { - logger.Error("[Middleware:BasicAuth] %v", err) - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(errorResponse)) - return - } - - if user == nil { - logger.Info("[Middleware:BasicAuth] User not found: %s", username) - w.WriteHeader(http.StatusUnauthorized) - w.Write([]byte(errorResponse)) - return - } - - logger.Info("[Middleware:BasicAuth] User authenticated: %s", username) - b.store.SetLastLogin(user.ID) - - ctx := r.Context() - ctx = context.WithValue(ctx, UserIDContextKey, user.ID) - ctx = context.WithValue(ctx, UserTimezoneContextKey, user.Timezone) - ctx = context.WithValue(ctx, IsAdminUserContextKey, user.IsAdmin) - ctx = context.WithValue(ctx, IsAuthenticatedContextKey, true) - - next.ServeHTTP(w, r.WithContext(ctx)) - }) -} - -// NewBasicAuthMiddleware returns a new BasicAuthMiddleware. -func NewBasicAuthMiddleware(s *storage.Storage) *BasicAuthMiddleware { - return &BasicAuthMiddleware{store: s} -} diff --git a/http/middleware/context_keys.go b/http/middleware/context_keys.go deleted file mode 100644 index 887a90e..0000000 --- a/http/middleware/context_keys.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017 Frédéric Guillot. All rights reserved. -// Use of this source code is governed by the Apache 2.0 -// license that can be found in the LICENSE file. - -package middleware - -// ContextKey represents a context key. -type ContextKey struct { - name string -} - -func (c ContextKey) String() string { - return c.name -} - -var ( - // UserIDContextKey is the context key used to store the user ID. - UserIDContextKey = &ContextKey{"UserID"} - - // UserTimezoneContextKey is the context key used to store the user timezone. - UserTimezoneContextKey = &ContextKey{"UserTimezone"} - - // IsAdminUserContextKey is the context key used to store the user role. - IsAdminUserContextKey = &ContextKey{"IsAdminUser"} - - // IsAuthenticatedContextKey is the context key used to store the authentication flag. - IsAuthenticatedContextKey = &ContextKey{"IsAuthenticated"} - - // UserSessionTokenContextKey is the context key used to store the user session ID. - UserSessionTokenContextKey = &ContextKey{"UserSessionToken"} - - // UserLanguageContextKey is the context key to store user language. - UserLanguageContextKey = &ContextKey{"UserLanguageContextKey"} - - // SessionIDContextKey is the context key used to store the session ID. - SessionIDContextKey = &ContextKey{"SessionID"} - - // CSRFContextKey is the context key used to store CSRF token. - CSRFContextKey = &ContextKey{"CSRF"} - - // OAuth2StateContextKey is the context key used to store OAuth2 state. - OAuth2StateContextKey = &ContextKey{"OAuth2State"} - - // FlashMessageContextKey is the context key used to store a flash message. - FlashMessageContextKey = &ContextKey{"FlashMessage"} - - // FlashErrorMessageContextKey is the context key used to store a flash error message. - FlashErrorMessageContextKey = &ContextKey{"FlashErrorMessage"} -) diff --git a/http/middleware/fever.go b/http/middleware/fever.go deleted file mode 100644 index 54eb0ca..0000000 --- a/http/middleware/fever.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2017 Frédéric Guillot. All rights reserved. -// Use of this source code is governed by the Apache 2.0 -// license that can be found in the LICENSE file. - -package middleware - -import ( - "context" - "net/http" - - "github.com/miniflux/miniflux/logger" - "github.com/miniflux/miniflux/storage" -) - -// FeverMiddleware is the middleware that handles Fever API. -type FeverMiddleware struct { - store *storage.Storage -} - -// Handler executes the middleware. -func (f *FeverMiddleware) Handler(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - logger.Debug("[Middleware:Fever]") - - apiKey := r.FormValue("api_key") - user, err := f.store.UserByFeverToken(apiKey) - if err != nil { - logger.Error("[Fever] %v", err) - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{"api_version": 3, "auth": 0}`)) - return - } - - if user == nil { - logger.Info("[Middleware:Fever] Fever authentication failure") - w.Header().Set("Content-Type", "application/json") - w.Write([]byte(`{"api_version": 3, "auth": 0}`)) - return - } - - logger.Info("[Middleware:Fever] User #%d is authenticated", user.ID) - f.store.SetLastLogin(user.ID) - - ctx := r.Context() - ctx = context.WithValue(ctx, UserIDContextKey, user.ID) - ctx = context.WithValue(ctx, UserTimezoneContextKey, user.Timezone) - ctx = context.WithValue(ctx, IsAdminUserContextKey, user.IsAdmin) - ctx = context.WithValue(ctx, IsAuthenticatedContextKey, true) - - next.ServeHTTP(w, r.WithContext(ctx)) - }) -} - -// NewFeverMiddleware returns a new FeverMiddleware. -func NewFeverMiddleware(s *storage.Storage) *FeverMiddleware { - return &FeverMiddleware{store: s} -} diff --git a/http/middleware/middleware.go b/http/middleware/middleware.go deleted file mode 100644 index 9853bc3..0000000 --- a/http/middleware/middleware.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2017 Frédéric Guillot. All rights reserved. -// Use of this source code is governed by the Apache 2.0 -// license that can be found in the LICENSE file. - -package middleware - -import ( - "net/http" -) - -// Middleware represents a HTTP middleware. -type Middleware func(http.Handler) http.Handler - -// Chain handles a list of middlewares. -type Chain struct { - middlewares []Middleware -} - -// Wrap adds a HTTP handler into the chain. -func (m *Chain) Wrap(h http.Handler) http.Handler { - for i := range m.middlewares { - h = m.middlewares[len(m.middlewares)-1-i](h) - } - - return h -} - -// WrapFunc adds a HTTP handler function into the chain. -func (m *Chain) WrapFunc(fn http.HandlerFunc) http.Handler { - return m.Wrap(fn) -} - -// NewChain returns a new Chain. -func NewChain(middlewares ...Middleware) *Chain { - return &Chain{append(([]Middleware)(nil), middlewares...)} -} diff --git a/http/middleware/session.go b/http/middleware/session.go deleted file mode 100644 index 9b7dd86..0000000 --- a/http/middleware/session.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2017 Frédéric Guillot. All rights reserved. -// Use of this source code is governed by the Apache 2.0 -// license that can be found in the LICENSE file. - -package middleware - -import ( - "context" - "net/http" - - "github.com/miniflux/miniflux/config" - "github.com/miniflux/miniflux/http/cookie" - "github.com/miniflux/miniflux/logger" - "github.com/miniflux/miniflux/model" - "github.com/miniflux/miniflux/storage" -) - -// SessionMiddleware represents a session middleware. -type SessionMiddleware struct { - cfg *config.Config - store *storage.Storage -} - -// Handler execute the middleware. -func (s *SessionMiddleware) Handler(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var err error - session := s.getSessionValueFromCookie(r) - - if session == nil { - logger.Debug("[Middleware:Session] Session not found") - session, err = s.store.CreateSession() - if err != nil { - logger.Error("[Middleware:Session] %v", err) - http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - http.SetCookie(w, cookie.New(cookie.CookieSessionID, session.ID, s.cfg.IsHTTPS, s.cfg.BasePath())) - } else { - logger.Debug("[Middleware:Session] %s", session) - } - - if r.Method == "POST" { - formValue := r.FormValue("csrf") - headerValue := r.Header.Get("X-Csrf-Token") - - if session.Data.CSRF != formValue && session.Data.CSRF != headerValue { - logger.Error(`[Middleware:Session] Invalid or missing CSRF token: Form="%s", Header="%s"`, formValue, headerValue) - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("Invalid or missing CSRF session!")) - return - } - } - - ctx := r.Context() - ctx = context.WithValue(ctx, SessionIDContextKey, session.ID) - ctx = context.WithValue(ctx, CSRFContextKey, session.Data.CSRF) - ctx = context.WithValue(ctx, OAuth2StateContextKey, session.Data.OAuth2State) - ctx = context.WithValue(ctx, FlashMessageContextKey, session.Data.FlashMessage) - ctx = context.WithValue(ctx, FlashErrorMessageContextKey, session.Data.FlashErrorMessage) - ctx = context.WithValue(ctx, UserLanguageContextKey, session.Data.Language) - next.ServeHTTP(w, r.WithContext(ctx)) - }) -} - -func (s *SessionMiddleware) getSessionValueFromCookie(r *http.Request) *model.Session { - sessionCookie, err := r.Cookie(cookie.CookieSessionID) - if err == http.ErrNoCookie { - return nil - } - - session, err := s.store.Session(sessionCookie.Value) - if err != nil { - logger.Error("[Middleware:Session] %v", err) - return nil - } - - return session -} - -// NewSessionMiddleware returns a new SessionMiddleware. -func NewSessionMiddleware(cfg *config.Config, store *storage.Storage) *SessionMiddleware { - return &SessionMiddleware{cfg, store} -} diff --git a/http/middleware/user_session.go b/http/middleware/user_session.go deleted file mode 100644 index 820d093..0000000 --- a/http/middleware/user_session.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2017 Frédéric Guillot. All rights reserved. -// Use of this source code is governed by the Apache 2.0 -// license that can be found in the LICENSE file. - -package middleware - -import ( - "context" - "net/http" - - "github.com/miniflux/miniflux/http/cookie" - "github.com/miniflux/miniflux/http/route" - "github.com/miniflux/miniflux/logger" - "github.com/miniflux/miniflux/model" - "github.com/miniflux/miniflux/storage" - - "github.com/gorilla/mux" -) - -// UserSessionMiddleware represents a user session middleware. -type UserSessionMiddleware struct { - store *storage.Storage - router *mux.Router -} - -// Handler execute the middleware. -func (s *UserSessionMiddleware) Handler(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - session := s.getSessionFromCookie(r) - - if session == nil { - logger.Debug("[Middleware:UserSession] Session not found") - if s.isPublicRoute(r) { - next.ServeHTTP(w, r) - } else { - http.Redirect(w, r, route.Path(s.router, "login"), http.StatusFound) - } - } else { - logger.Debug("[Middleware:UserSession] %s", session) - ctx := r.Context() - ctx = context.WithValue(ctx, UserIDContextKey, session.UserID) - ctx = context.WithValue(ctx, IsAuthenticatedContextKey, true) - ctx = context.WithValue(ctx, UserSessionTokenContextKey, session.Token) - - next.ServeHTTP(w, r.WithContext(ctx)) - } - }) -} - -func (s *UserSessionMiddleware) isPublicRoute(r *http.Request) bool { - route := mux.CurrentRoute(r) - switch route.GetName() { - case "login", - "checkLogin", - "stylesheet", - "javascript", - "oauth2Redirect", - "oauth2Callback", - "appIcon", - "favicon", - "webManifest": - return true - default: - return false - } -} - -func (s *UserSessionMiddleware) getSessionFromCookie(r *http.Request) *model.UserSession { - sessionCookie, err := r.Cookie(cookie.CookieUserSessionID) - if err == http.ErrNoCookie { - return nil - } - - session, err := s.store.UserSessionByToken(sessionCookie.Value) - if err != nil { - logger.Error("[Middleware:UserSession] %v", err) - return nil - } - - return session -} - -// NewUserSessionMiddleware returns a new UserSessionMiddleware. -func NewUserSessionMiddleware(s *storage.Storage, r *mux.Router) *UserSessionMiddleware { - return &UserSessionMiddleware{store: s, router: r} -} |