diff --git a/debian/changelog b/debian/changelog index 92470ea..9a43c7f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,32 +4,36 @@ lql-api (0.0.7) UNRELEASED; urgency=medium * Add filtering * Try reconnect on EPIPE maybe fixes conn problems * Improve example.sh + * Document localserver installing + * Properly reconnect in lql.Client + * Add help outputs to the README + * Update docs - -- Webmeisterei Support Wed, 30 Sep 2020 14:03:30 +0200 + -- Webmeisterei Support Wed, 30 Sep 2020 16:15:17 +0200 lql-api (0.0.6) UNRELEASED; urgency=medium * Use GncpPool.Put, maybe fixes connection problems - -- Webmeisterei Support Wed, 30 Sep 2020 05:24:30 +0200 + -- Webmeisterei Support Wed, 30 Sep 2020 05:24:30 +0200 lql-api (0.0.5) UNRELEASED; urgency=medium * Rename host -> hosts in stats/tactical_overview - -- Webmeisterei Support Wed, 30 Sep 2020 05:24:30 +0200 + -- Webmeisterei Support Wed, 30 Sep 2020 05:24:30 +0200 lql-api (0.0.4) UNRELEASED; urgency=medium * Add /v1/table/:name and /v1/table/:name/columns - -- Webmeisterei Support Wed, 30 Sep 2020 05:24:30 +0200 + -- Webmeisterei Support Wed, 30 Sep 2020 05:24:30 +0200 lql-api (0.0.3) UNRELEASED; urgency=medium * Add ping API - -- Webmeisterei Support Tue, 30 Sep 2020 01:31:46 +0200 + -- Webmeisterei Support Tue, 30 Sep 2020 01:31:46 +0200 lql-api (0.0~git20200929.ca3764e-1) UNRELEASED; urgency=medium diff --git a/go.mod b/go.mod index 356f0bd..caa4dd0 100644 --- a/go.mod +++ b/go.mod @@ -13,4 +13,5 @@ require ( github.com/toorop/gin-logrus v0.0.0-20200831135515-d2ee50d38dae github.com/wI2L/fizz v0.13.4 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a + gopkg.in/fsnotify.v1 v1.4.7 ) diff --git a/lql/client.go b/lql/client.go index 858294a..51d8e5c 100644 --- a/lql/client.go +++ b/lql/client.go @@ -190,7 +190,6 @@ func (c *Client) RequestRaw(context context.Context, request, outputFormat, auth if err != nil { return nil, err } - conn.Close() _, err = conn.Write([]byte(request)) if err != nil && errors.Is(err, syscall.EPIPE) { diff --git a/lql/middleware.go b/lql/middleware.go index 18f3203..5853d4e 100644 --- a/lql/middleware.go +++ b/lql/middleware.go @@ -2,12 +2,14 @@ package lql import ( "errors" + "fmt" "net/http" - "strconv" + "sync" auth "github.com/abbot/go-http-auth" "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" + "gopkg.in/fsnotify.v1" ) var ( @@ -34,11 +36,69 @@ func clientInjectorMiddleware(client *Client) gin.HandlerFunc { } } -func basicAuthMiddleware(a *auth.BasicAuth) gin.HandlerFunc { - realmHeader := "Basic realm=" + strconv.Quote(a.Realm) +func basicAuthMiddleware(htpasswdPath, realm string) gin.HandlerFunc { + htpasswd := auth.HtpasswdFileProvider(htpasswdPath) + authenticator := auth.NewBasicAuthenticator(realm, htpasswd) + realmHeader := fmt.Sprintf("Basic realm=\"%s\"", realm) return func(c *gin.Context) { - user := a.CheckAuth(c.Request) + user := authenticator.CheckAuth(c.Request) + + if user == "" { + c.Header("WWW-Authenticate", realmHeader) + c.AbortWithStatus(http.StatusUnauthorized) + return + } + + c.Set("user", user) + } +} + +func basicAuthWithWatcherMiddleware(htpasswdPath, realm string) gin.HandlerFunc { + authenticatorLock := sync.RWMutex{} + htpasswd := auth.HtpasswdFileProvider(htpasswdPath) + authenticator := auth.NewBasicAuthenticator(realm, htpasswd) + + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + defer watcher.Close() + + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + Logger.WithField("event", event).Debug("event") + if event.Op&fsnotify.Write == fsnotify.Write { + authenticatorLock.Lock() + Logger.WithField("path", event.Name).Debug("Modified file") + htpasswd = auth.HtpasswdFileProvider(htpasswdPath) + authenticator = auth.NewBasicAuthenticator(realm, htpasswd) + authenticatorLock.Unlock() + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + Logger.WithField("error", err).Error() + } + } + }() + + err = watcher.Add(htpasswdPath) + if err != nil { + log.Fatal(err) + } + + realmHeader := fmt.Sprintf("Basic realm=\"%s\"", realm) + return func(c *gin.Context) { + authenticatorLock.RLock() + user := authenticator.CheckAuth(c.Request) + authenticatorLock.RUnlock() if user == "" { c.Header("WWW-Authenticate", realmHeader) diff --git a/lql/server.go b/lql/server.go index 06ca1cf..1428d5a 100644 --- a/lql/server.go +++ b/lql/server.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" - auth "github.com/abbot/go-http-auth" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" @@ -48,9 +47,7 @@ All v1/ endpoints require http basic auth`, // Setup routes. v1Group := fizz.Group("/v1", "v1", "LQL API v1") if htpasswdPath != "" { - htpasswd := auth.HtpasswdFileProvider(htpasswdPath) - authenticator := auth.NewBasicAuthenticator("LQL API", htpasswd) - v1Group.Use(basicAuthMiddleware(authenticator)) + v1Group.Use(basicAuthWithWatcherMiddleware(htpasswdPath, "LQL API")) } else { // Inject empty user if not .htpasswd have been given v1Group.Use(func(c *gin.Context) { @@ -67,6 +64,10 @@ All v1/ endpoints require http basic auth`, return &Server{fizz: fizz, htpasswdPath: htpasswdPath}, nil } +func (s *Server) GetRouter() *fizz.Fizz { + return s.fizz +} + func (s *Server) ListenAndServe(address string) { srv := &http.Server{ Addr: address,