From ed52c4a590b3473f1541f56fa4d0b2eb104a17b9 Mon Sep 17 00:00:00 2001 From: Umputun Date: Thu, 15 Feb 2018 23:40:12 -0600 Subject: [PATCH] extract all auth related deps into Authenticator obj --- app/main.go | 22 +++++++++++---------- app/rest/admin.go | 5 ++--- app/rest/auth/middleware.go | 17 +++++++++++++---- app/rest/avatar/avatar.go | 4 ++-- app/rest/avatar/avatar_test.go | 6 ++++-- app/rest/middleware.go | 8 ++++++-- app/rest/server.go | 35 +++++++++++++++------------------- remark.rest | 2 +- 8 files changed, 55 insertions(+), 44 deletions(-) diff --git a/app/main.go b/app/main.go index f17f89c3..7cd56008 100644 --- a/app/main.go +++ b/app/main.go @@ -107,16 +107,18 @@ func main() { } srv := rest.Server{ - Version: revision, - DataService: dataService, - SessionStore: sessionStore, - Admins: opts.Admins, - DevMode: opts.DevMode, - Exporter: &exporter, - AuthProviders: makeAuthProviders(sessionStore, avatarProxy), - Cache: common.NewLoadingCache(4*time.Hour, 15*time.Minute, postFlushFn), - AvatarProxy: avatarProxy, - Notifier: notifier.NewNoperation(), + Version: revision, + DataService: dataService, + DevMode: opts.DevMode, + Exporter: &exporter, + Authenticator: auth.Authenticator{ + Admins: opts.Admins, + SessionStore: sessionStore, + Providers: makeAuthProviders(sessionStore, avatarProxy), + AvatarProxy: avatarProxy, + }, + Cache: common.NewLoadingCache(4*time.Hour, 15*time.Minute, postFlushFn), + Notifier: notifier.NewNoperation(), } if opts.DevMode { diff --git a/app/rest/admin.go b/app/rest/admin.go index 4af8548a..fb1f0f67 100644 --- a/app/rest/admin.go +++ b/app/rest/admin.go @@ -12,7 +12,6 @@ import ( "github.com/go-chi/render" "github.com/umputun/remark/app/migrator" - "github.com/umputun/remark/app/rest/auth" "github.com/umputun/remark/app/rest/common" "github.com/umputun/remark/app/store" ) @@ -25,9 +24,9 @@ type admin struct { cache common.LoadingCache } -func (a *admin) routes() chi.Router { +func (a *admin) routes(middlewares ...func(http.Handler) http.Handler) chi.Router { router := chi.NewRouter() - router.Use(auth.AdminOnly) + router.Use(middlewares...) router.Delete("/comment/{id}", a.deleteCommentCtrl) router.Put("/user/{userid}", a.setBlockCtrl) router.Get("/export", a.exportCtrl) diff --git a/app/rest/auth/middleware.go b/app/rest/auth/middleware.go index e9072e7f..83e447f5 100644 --- a/app/rest/auth/middleware.go +++ b/app/rest/auth/middleware.go @@ -6,6 +6,7 @@ import ( "github.com/gorilla/sessions" + "github.com/umputun/remark/app/rest/avatar" "github.com/umputun/remark/app/rest/common" "github.com/umputun/remark/app/store" ) @@ -28,8 +29,16 @@ var devUser = store.User{ Admin: true, } +// Authenticator is top level auth object providing middlewares +type Authenticator struct { + SessionStore sessions.Store + AvatarProxy *avatar.Proxy + Admins []string + Providers []Provider +} + // Auth middleware adds auth from session and populates user info -func Auth(sessionStore sessions.Store, admins []string, modes []Mode) func(http.Handler) http.Handler { +func (a *Authenticator) Auth(modes []Mode) func(http.Handler) http.Handler { inModes := func(mode Mode) bool { for _, m := range modes { @@ -53,7 +62,7 @@ func Auth(sessionStore sessions.Store, admins []string, modes []Mode) func(http. return } - session, err := sessionStore.Get(r, "remark") + session, err := a.SessionStore.Get(r, "remark") if err != nil && inModes(Full) { // in full auth lack of session causes Unauthorized http.Error(w, err.Error(), http.StatusUnauthorized) return @@ -72,7 +81,7 @@ func Auth(sessionStore sessions.Store, admins []string, modes []Mode) func(http. if ok { // if uinfo in session, populate to context user := uinfoData.(store.User) - for _, admin := range admins { + for _, admin := range a.Admins { if admin == user.ID { user.Admin = true break @@ -91,7 +100,7 @@ func Auth(sessionStore sessions.Store, admins []string, modes []Mode) func(http. } // AdminOnly allows access to admins -func AdminOnly(next http.Handler) http.Handler { +func (a *Authenticator) AdminOnly(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { user, err := common.GetUserInfo(r) diff --git a/app/rest/avatar/avatar.go b/app/rest/avatar/avatar.go index 40eaef32..f35cbe76 100644 --- a/app/rest/avatar/avatar.go +++ b/app/rest/avatar/avatar.go @@ -83,7 +83,7 @@ func (p *Proxy) Put(u store.User) (avatarURL string, err error) { } // Routes returns auth routes for given provider -func (p *Proxy) Routes() chi.Router { +func (p *Proxy) Routes() (string, chi.Router) { router := chi.NewRouter() // GET /123456789.image @@ -118,7 +118,7 @@ func (p *Proxy) Routes() chi.Router { } }) - return router + return p.RoutePath, router } // encodeID hashes user id to sha1 diff --git a/app/rest/avatar/avatar_test.go b/app/rest/avatar/avatar_test.go index 77ba8b93..b1e58b6a 100644 --- a/app/rest/avatar/avatar_test.go +++ b/app/rest/avatar/avatar_test.go @@ -66,7 +66,8 @@ func TestRoutes(t *testing.T) { } rr := httptest.NewRecorder() - handler := http.Handler(p.Routes()) + _, routes := p.Routes() + handler := http.Handler(routes) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code) @@ -92,7 +93,8 @@ func TestRoutesDefault(t *testing.T) { } rr := httptest.NewRecorder() - handler := http.Handler(p.Routes()) + _, routes := p.Routes() + handler := http.Handler(routes) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code) diff --git a/app/rest/middleware.go b/app/rest/middleware.go index e5ff5a84..230a7866 100644 --- a/app/rest/middleware.go +++ b/app/rest/middleware.go @@ -141,9 +141,13 @@ func Logger(flags ...LoggerFlag) func(http.Handler) http.Handler { q = qun } + remoteIP := strings.Split(r.RemoteAddr, ":")[0] + if strings.HasPrefix(r.RemoteAddr, "[") { + remoteIP = strings.Split(r.RemoteAddr, "]:")[0] + "]" + } + log.Printf("[INFO] REST %s - %s - %s - %d (%d) - %v %s %s", - r.Method, q, strings.Split(r.RemoteAddr, ":")[0], - ww.Status(), ww.BytesWritten(), t2.Sub(t1), user, body) + r.Method, q, remoteIP, ww.Status(), ww.BytesWritten(), t2.Sub(t1), user, body) }() h.ServeHTTP(ww, r) diff --git a/app/rest/server.go b/app/rest/server.go index 2818c324..d7bdaaa7 100644 --- a/app/rest/server.go +++ b/app/rest/server.go @@ -17,14 +17,12 @@ import ( "github.com/go-chi/chi/middleware" "github.com/go-chi/render" "github.com/gorilla/context" - "github.com/gorilla/sessions" "github.com/pkg/errors" "gopkg.in/russross/blackfriday.v2" "github.com/umputun/remark/app/migrator" "github.com/umputun/remark/app/notifier" "github.com/umputun/remark/app/rest/auth" - "github.com/umputun/remark/app/rest/avatar" "github.com/umputun/remark/app/rest/common" "github.com/umputun/remark/app/rest/format" "github.com/umputun/remark/app/store" @@ -32,18 +30,15 @@ import ( // Server is a rest access server type Server struct { - Version string + Version string + DevMode bool + DataService store.Service - Admins []string - AuthProviders []auth.Provider - SessionStore sessions.Store + Authenticator auth.Authenticator Exporter migrator.Exporter Cache common.LoadingCache - AvatarProxy *avatar.Proxy Notifier notifier.Interface - DevMode bool - httpServer *http.Server mod admin } @@ -61,8 +56,8 @@ func (s *Server) Run(port int) { return modes } - if len(s.Admins) > 0 { - log.Printf("[DEBUG] admins %+v", s.Admins) + if len(s.Authenticator.Admins) > 0 { + log.Printf("[DEBUG] admins %+v", s.Authenticator.Admins) } router := chi.NewRouter() @@ -71,23 +66,23 @@ func (s *Server) Run(port int) { router.Use(tollbooth_chi.LimitHandler(tollbooth.NewLimiter(10, nil))) // all request by default allow anonymous access - router.Use(auth.Auth(s.SessionStore, s.Admins, maybeWithDevMode(auth.Anonymous))) + router.Use(s.Authenticator.Auth(maybeWithDevMode(auth.Anonymous))) router.Use(AppInfo("remark42", s.Version), Ping, Logger(LogAll)) router.Use(context.ClearHandler) // if you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler // auth routes for all providers router.Route("/auth", func(r chi.Router) { - for _, provider := range s.AuthProviders { + for _, provider := range s.Authenticator.Providers { r.Mount("/"+provider.Name, provider.Routes()) // mount auth providers as /auth/{name} } - if len(s.AuthProviders) > 0 { + if len(s.Authenticator.Providers) > 0 { // shortcut, can be any of providers, all logouts do the same - removes cookie - r.Get("/logout", s.AuthProviders[0].LogoutHandler) + r.Get("/logout", s.Authenticator.Providers[0].LogoutHandler) } }) - router.Mount(s.AvatarProxy.RoutePath, s.AvatarProxy.Routes()) + router.Mount(s.Authenticator.AvatarProxy.Routes()) // api routes router.Route("/api/v1", func(rapi chi.Router) { @@ -102,7 +97,7 @@ func (s *Server) Run(port int) { rapi.Get("/config", s.configCtrl) // protected routes, require auth - rapi.With(auth.Auth(s.SessionStore, s.Admins, maybeWithDevMode(auth.Full))).Group(func(rauth chi.Router) { + rapi.With(s.Authenticator.Auth(maybeWithDevMode(auth.Full))).Group(func(rauth chi.Router) { rauth.Post("/comment", s.createCommentCtrl) rauth.Put("/comment/{id}", s.updateCommentCtrl) rauth.Get("/user", s.userInfoCtrl) @@ -111,7 +106,7 @@ func (s *Server) Run(port int) { rauth.Get("/notify", s.notifyStatusCtrl) // admin routes, admin users only s.mod = admin{dataService: s.DataService, exporter: s.Exporter, cache: s.Cache} - rauth.Mount("/admin", s.mod.routes()) + rauth.Mount("/admin", s.mod.routes(s.Authenticator.AdminOnly)) }) }) @@ -343,10 +338,10 @@ func (s *Server) configCtrl(w http.ResponseWriter, r *http.Request) { cnf := config{ Version: s.Version, EditDuration: int(s.DataService.EditDuration.Seconds()), - Admins: s.Admins, + Admins: s.Authenticator.Admins, } authNames := []string{} - for _, ap := range s.AuthProviders { + for _, ap := range s.Authenticator.Providers { authNames = append(authNames, ap.Name) } cnf.Auth = authNames diff --git a/remark.rest b/remark.rest index 75423f97..aa9d0627 100644 --- a/remark.rest +++ b/remark.rest @@ -9,7 +9,7 @@ GET {{host}}/api/v1/find?site=remark&sort=time&format=plain&url=https://radio-t. GET {{host}}/api/v1/last/50?site=remark ### create comment -POST 127.0.0.1:8080/api/v1/comment +POST {{host}}/api/v1/comment Content-Type: application/json {