diff --git a/authentication.go b/authentication.go index 0eef9f2..bf56678 100644 --- a/authentication.go +++ b/authentication.go @@ -7,17 +7,22 @@ import ( "encoding/json" "io" "net/http" + "strings" "github.com/pquerna/otp/totp" "go.goblog.app/app/pkgs/contenttype" ) +const loggedInKey contextKey = "loggedIn" + +// Check if credentials are correct func (a *goBlog) checkCredentials(username, password, totpPasscode string) bool { return username == a.cfg.User.Nick && password == a.cfg.User.Password && (a.cfg.User.TOTP == "" || totp.Validate(totpPasscode, a.cfg.User.TOTP)) } +// Check if app passwords are correct func (a *goBlog) checkAppPasswords(username, password string) bool { for _, apw := range a.cfg.User.AppPasswords { if apw.Username == username && apw.Password == password { @@ -27,31 +32,29 @@ func (a *goBlog) checkAppPasswords(username, password string) bool { return false } -func (a *goBlog) jwtKey() []byte { - return []byte(a.cfg.Server.JWTSecret) +// Check if cookie is known and logged in +func (a *goBlog) checkLoginCookie(r *http.Request) bool { + ses, err := a.loginSessions.Get(r, "l") + if err == nil && ses != nil { + if login, ok := ses.Values["login"]; ok && login.(bool) { + return true + } + } + return false } +// Middleware to force login func (a *goBlog) authMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // 1. Check if already logged in - if loggedIn, ok := r.Context().Value(loggedInKey).(bool); ok && loggedIn { + // Check if already logged in + if a.isLoggedIn(r) { next.ServeHTTP(w, r) return } - // 2. Check BasicAuth (just for app passwords) - if username, password, ok := r.BasicAuth(); ok && a.checkAppPasswords(username, password) { - next.ServeHTTP(w, r) - return - } - // 3. Check login cookie - if a.checkLoginCookie(r) { - next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), loggedInKey, true))) - return - } - // 4. Show login form + // Show login form w.Header().Set("Cache-Control", "no-store,max-age=0") - h, _ := json.Marshal(r.Header.Clone()) - b, _ := io.ReadAll(io.LimitReader(r.Body, 2000000)) // Only allow 20 Megabyte + h, _ := json.Marshal(r.Header) + b, _ := io.ReadAll(io.LimitReader(r.Body, 20*1000*1000)) // Only allow 20 MB _ = r.Body.Close() if len(b) == 0 { // Maybe it's a form @@ -69,28 +72,7 @@ func (a *goBlog) authMiddleware(next http.Handler) http.Handler { }) } -const loggedInKey contextKey = "loggedIn" - -func (a *goBlog) checkLoggedIn(next http.Handler) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - if a.checkLoginCookie(r) { - next.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), loggedInKey, true))) - return - } - next.ServeHTTP(rw, r) - }) -} - -func (a *goBlog) checkLoginCookie(r *http.Request) bool { - ses, err := a.loginSessions.Get(r, "l") - if err == nil && ses != nil { - if login, ok := ses.Values["login"]; ok && login.(bool) { - return true - } - } - return false -} - +// Middleware to check if the request is a login request func (a *goBlog) checkIsLogin(next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { if !a.checkLogin(rw, r) { @@ -99,11 +81,12 @@ func (a *goBlog) checkIsLogin(next http.Handler) http.Handler { }) } +// Checks login and returns true if it already served an error func (a *goBlog) checkLogin(w http.ResponseWriter, r *http.Request) bool { if r.Method != http.MethodPost { return false } - if r.Header.Get(contentType) != contenttype.WWWForm { + if !strings.Contains(r.Header.Get(contentType), contenttype.WWWForm) { return false } if r.FormValue("loginaction") != "login" { @@ -131,22 +114,49 @@ func (a *goBlog) checkLogin(w http.ResponseWriter, r *http.Request) bool { return true } ses.Values["login"] = true - cookie, err := a.loginSessions.SaveGetCookie(r, w, ses) + err = a.loginSessions.Save(r, w, ses) if err != nil { a.serveError(w, r, err.Error(), http.StatusInternalServerError) return true } - req.AddCookie(cookie) // Serve original request + setLoggedIn(req) a.d.ServeHTTP(w, req) return true } +func (a *goBlog) isLoggedIn(r *http.Request) bool { + // Check if context key already set + if loggedIn, ok := r.Context().Value(loggedInKey).(bool); ok && loggedIn { + return true + } + // Check app passwords + if username, password, ok := r.BasicAuth(); ok && a.checkAppPasswords(username, password) { + setLoggedIn(r) + return true + } + // Check session cookie + if a.checkLoginCookie(r) { + setLoggedIn(r) + return true + } + // Not logged in + return false +} + +// Set request context value +func setLoggedIn(r *http.Request) { + newRequest := r.WithContext(context.WithValue(r.Context(), loggedInKey, true)) + (*r) = *newRequest +} + +// HandlerFunc to redirect to home after login // Need to set auth middleware! func serveLogin(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/", http.StatusFound) } +// HandlerFunc to delete login session and cookie func (a *goBlog) serveLogout(w http.ResponseWriter, r *http.Request) { if ses, err := a.loginSessions.Get(r, "l"); err == nil && ses != nil { _ = a.loginSessions.Delete(r, w, ses) diff --git a/authentication_test.go b/authentication_test.go new file mode 100644 index 0000000..e93ab95 --- /dev/null +++ b/authentication_test.go @@ -0,0 +1,194 @@ +package main + +import ( + "io" + "net/http" + "net/http/httptest" + "net/url" + "path/filepath" + "strings" + "testing" + + "github.com/justinas/alice" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.goblog.app/app/pkgs/contenttype" +) + +func Test_authMiddleware(t *testing.T) { + app := &goBlog{ + cfg: &config{ + Db: &configDb{ + File: filepath.Join(t.TempDir(), "test.db"), + }, + Server: &configServer{ + PublicAddress: "https://example.com", + }, + Blogs: map[string]*configBlog{ + "en": { + Lang: "en", + }, + }, + DefaultBlog: "en", + User: &configUser{ + Nick: "test", + Password: "pass", + AppPasswords: []*configAppPassword{ + { + Username: "app1", + Password: "pass1", + }, + }, + }, + }, + } + + _ = app.initDatabase(false) + app.initSessions() + _ = app.initTemplateStrings() + _ = app.initRendering() + + app.d = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + _, _ = rw.Write([]byte("ABC Test")) + if app.isLoggedIn(r) { + _, _ = rw.Write([]byte("Logged in")) + } + }) + + h := alice.New(app.checkIsLogin, app.authMiddleware).Then(app.d) + + t.Run("Login required", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/abc", nil) + + rec := httptest.NewRecorder() + + h.ServeHTTP(rec, req) + + res := rec.Result() + resBody, _ := io.ReadAll(res.Body) + _ = res.Body.Close() + resString := string(resBody) + + // Login form + assert.Equal(t, http.StatusOK, res.StatusCode) + assert.Contains(t, res.Header.Get("Content-Type"), contenttype.HTML) + assert.Contains(t, resString, "name=loginmethod value=POST") + }) + + t.Run("Login with app password", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/abc", nil) + + req.SetBasicAuth("app1", "pass1") + + rec := httptest.NewRecorder() + + h.ServeHTTP(rec, req) + + res := rec.Result() + resBody, _ := io.ReadAll(res.Body) + _ = res.Body.Close() + resString := string(resBody) + + assert.Equal(t, http.StatusOK, res.StatusCode) + assert.Contains(t, resString, "ABC Test") + assert.Contains(t, resString, "Logged in") + }) + + t.Run("Login with wrong app password", func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/abc", nil) + + req.SetBasicAuth("app1", "wrong") + + rec := httptest.NewRecorder() + + h.ServeHTTP(rec, req) + + res := rec.Result() + resBody, _ := io.ReadAll(res.Body) + _ = res.Body.Close() + resString := string(resBody) + + // Login form + assert.Equal(t, http.StatusOK, res.StatusCode) + assert.Contains(t, res.Header.Get("Content-Type"), contenttype.HTML) + assert.Contains(t, resString, "name=loginmethod value=POST") + }) + + t.Run("Login with form", func(t *testing.T) { + // First request + + data := url.Values{} + data.Add("loginaction", "login") + data.Add("loginmethod", "GET") + data.Add("username", "test") + data.Add("password", "pass") + + req := httptest.NewRequest(http.MethodPost, "/abc", strings.NewReader(data.Encode())) + req.Header.Add("Content-Type", contenttype.WWWForm) + + rec := httptest.NewRecorder() + + h.ServeHTTP(rec, req) + + res := rec.Result() + resBody, _ := io.ReadAll(res.Body) + _ = res.Body.Close() + resString := string(resBody) + + assert.Equal(t, http.StatusOK, res.StatusCode) + assert.Contains(t, resString, "ABC Test") + assert.Contains(t, resString, "Logged in") + + // Second request using cookie + + require.Len(t, res.Cookies(), 1) + + req = httptest.NewRequest(http.MethodPost, "/abc", nil) + req.AddCookie(res.Cookies()[0]) + + rec = httptest.NewRecorder() + + h.ServeHTTP(rec, req) + + res = rec.Result() + resBody, _ = io.ReadAll(res.Body) + _ = res.Body.Close() + resString = string(resBody) + + assert.Equal(t, http.StatusOK, res.StatusCode) + assert.Contains(t, resString, "ABC Test") + assert.Contains(t, resString, "Logged in") + }) + + t.Run("Login with wrong credentials", func(t *testing.T) { + data := url.Values{} + data.Add("loginaction", "login") + data.Add("loginmethod", "GET") + data.Add("username", "test") + data.Add("password", "wrong") + + req := httptest.NewRequest(http.MethodPost, "/abc", strings.NewReader(data.Encode())) + req.Header.Add("Content-Type", contenttype.WWWForm) + + rec := httptest.NewRecorder() + + h.ServeHTTP(rec, req) + + res := rec.Result() + resBody, _ := io.ReadAll(res.Body) + _ = res.Body.Close() + resString := string(resBody) + + assert.Equal(t, http.StatusUnauthorized, res.StatusCode) + assert.Contains(t, res.Header.Get("Content-Type"), contenttype.HTML) + assert.Contains(t, resString, "Incorrect credentials") + }) +} + +func Test_setLoggedIn(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/abc", nil) + setLoggedIn(req) + loggedIn, ok := req.Context().Value(loggedInKey).(bool) + assert.True(t, ok) + assert.True(t, loggedIn) +} diff --git a/blogroll.go b/blogroll.go index 15ad615..297ee6e 100644 --- a/blogroll.go +++ b/blogroll.go @@ -11,7 +11,6 @@ import ( "time" "github.com/kaorimatz/go-opml" - servertiming "github.com/mitchellh/go-server-timing" "github.com/thoas/go-funk" "go.goblog.app/app/pkgs/contenttype" ) @@ -20,18 +19,16 @@ const defaultBlogrollPath = "/blogroll" func (a *goBlog) serveBlogroll(w http.ResponseWriter, r *http.Request) { blog := r.Context().Value(blogContextKey).(string) - t := servertiming.FromContext(r.Context()).NewMetric("bg").Start() outlines, err, _ := a.blogrollCacheGroup.Do(blog, func() (interface{}, error) { return a.getBlogrollOutlines(blog) }) - t.Stop() if err != nil { log.Printf("Failed to get outlines: %v", err) a.serveError(w, r, "", http.StatusInternalServerError) return } if a.cfg.Cache != nil && a.cfg.Cache.Enable { - setInternalCacheExpirationHeader(w, r, int(a.cfg.Cache.Expiration)) + a.setInternalCacheExpirationHeader(w, r, int(a.cfg.Cache.Expiration)) } c := a.cfg.Blogs[blog].Blogroll can := a.getRelativePath(blog, defaultIfEmpty(c.Path, defaultBlogrollPath)) @@ -58,7 +55,7 @@ func (a *goBlog) serveBlogrollExport(w http.ResponseWriter, r *http.Request) { return } if a.cfg.Cache != nil && a.cfg.Cache.Enable { - setInternalCacheExpirationHeader(w, r, int(a.cfg.Cache.Expiration)) + a.setInternalCacheExpirationHeader(w, r, int(a.cfg.Cache.Expiration)) } w.Header().Set(contentType, contenttype.XMLUTF8) var opmlBytes bytes.Buffer diff --git a/blogstats.go b/blogstats.go index 1b190e9..1f8dbfa 100644 --- a/blogstats.go +++ b/blogstats.go @@ -5,8 +5,6 @@ import ( "database/sql" "encoding/gob" "net/http" - - servertiming "github.com/mitchellh/go-server-timing" ) const defaultBlogStatsPath = "/statistics" @@ -35,11 +33,9 @@ func (a *goBlog) serveBlogStats(w http.ResponseWriter, r *http.Request) { func (a *goBlog) serveBlogStatsTable(w http.ResponseWriter, r *http.Request) { blog := r.Context().Value(blogContextKey).(string) - t := servertiming.FromContext(r.Context()).NewMetric("bs").Start() data, err, _ := a.blogStatsCacheGroup.Do(blog, func() (interface{}, error) { return a.db.getBlogStats(blog) }) - t.Stop() if err != nil { a.serveError(w, r, err.Error(), http.StatusInternalServerError) return diff --git a/cache.go b/cache.go index d59f3f8..8ade9e4 100644 --- a/cache.go +++ b/cache.go @@ -40,7 +40,8 @@ func (a *goBlog) initCache() (err error) { return } -func (c *cache) cacheMiddleware(next http.Handler) http.Handler { +func (a *goBlog) cacheMiddleware(next http.Handler) http.Handler { + c := a.cache return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if c.c == nil { // No cache configured @@ -56,7 +57,7 @@ func (c *cache) cacheMiddleware(next http.Handler) http.Handler { next.ServeHTTP(w, r) return } - if loggedIn, ok := r.Context().Value(loggedInKey).(bool); ok && loggedIn { + if a.isLoggedIn(r) { next.ServeHTTP(w, r) return } @@ -179,7 +180,6 @@ func (c *cache) getCache(key string, next http.Handler, r *http.Request) (item * result.Header.Del("Accept-Ranges") result.Header.Del("ETag") result.Header.Del("Last-Modified") - result.Header.Del("Server-Timing") // Create cache item item = &cacheItem{ expiration: exp, @@ -205,8 +205,8 @@ func (c *cache) purge() { c.c.Clear() } -func setInternalCacheExpirationHeader(w http.ResponseWriter, r *http.Request, expiration int) { - if loggedIn, ok := r.Context().Value(loggedInKey).(bool); ok && loggedIn { +func (a *goBlog) setInternalCacheExpirationHeader(w http.ResponseWriter, r *http.Request, expiration int) { + if a.isLoggedIn(r) { return } w.Header().Set(cacheInternalExpirationHeader, strconv.Itoa(expiration)) diff --git a/captcha.go b/captcha.go index 38be5f3..bc1d90e 100644 --- a/captcha.go +++ b/captcha.go @@ -2,28 +2,38 @@ package main import ( "bytes" + "context" "encoding/base64" "encoding/json" "io" "net/http" + "strings" "github.com/dchest/captcha" "go.goblog.app/app/pkgs/contenttype" ) +const captchaSolvedKey contextKey = "captchaSolved" + func (a *goBlog) captchaMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // 1. Check Cookie + // Check if captcha already solved + if solved, ok := r.Context().Value(captchaSolvedKey).(bool); ok && solved { + next.ServeHTTP(w, r) + return + } + // Check Cookie ses, err := a.captchaSessions.Get(r, "c") if err == nil && ses != nil { if captcha, ok := ses.Values["captcha"]; ok && captcha.(bool) { - next.ServeHTTP(w, r) + next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), captchaSolvedKey, true))) return } } - // 2. Show Captcha - h, _ := json.Marshal(r.Header.Clone()) - b, _ := io.ReadAll(io.LimitReader(r.Body, 2000000)) // Only allow 20 Megabyte + // Show Captcha + w.Header().Set("Cache-Control", "no-store,max-age=0") + h, _ := json.Marshal(r.Header) + b, _ := io.ReadAll(io.LimitReader(r.Body, 20*1000*1000)) // Only allow 20 MB _ = r.Body.Close() if len(b) == 0 { // Maybe it's a form @@ -53,7 +63,7 @@ func (a *goBlog) checkCaptcha(w http.ResponseWriter, r *http.Request) bool { if r.Method != http.MethodPost { return false } - if r.Header.Get(contentType) != contenttype.WWWForm { + if !strings.Contains(r.Header.Get(contentType), contenttype.WWWForm) { return false } if r.FormValue("captchaaction") != "captcha" { @@ -77,12 +87,12 @@ func (a *goBlog) checkCaptcha(w http.ResponseWriter, r *http.Request) bool { return true } ses.Values["captcha"] = true - cookie, err := a.captchaSessions.SaveGetCookie(r, w, ses) + err = a.captchaSessions.Save(r, w, ses) if err != nil { a.serveError(w, r, err.Error(), http.StatusInternalServerError) return true } - req.AddCookie(cookie) + req = req.WithContext(context.WithValue(req.Context(), captchaSolvedKey, true)) } // Serve original request a.d.ServeHTTP(w, req) diff --git a/customPages.go b/customPages.go index 3f9df38..f35c7a8 100644 --- a/customPages.go +++ b/customPages.go @@ -8,9 +8,9 @@ func (a *goBlog) serveCustomPage(w http.ResponseWriter, r *http.Request) { page := r.Context().Value(customPageContextKey).(*configCustomPage) if a.cfg.Cache != nil && a.cfg.Cache.Enable && page.Cache { if page.CacheExpiration != 0 { - setInternalCacheExpirationHeader(w, r, page.CacheExpiration) + a.setInternalCacheExpirationHeader(w, r, page.CacheExpiration) } else { - setInternalCacheExpirationHeader(w, r, int(a.cfg.Cache.Expiration)) + a.setInternalCacheExpirationHeader(w, r, int(a.cfg.Cache.Expiration)) } } a.render(w, r, page.Template, &renderData{ diff --git a/dbmigrations/00022.sql b/dbmigrations/00022.sql new file mode 100644 index 0000000..d5c3c22 --- /dev/null +++ b/dbmigrations/00022.sql @@ -0,0 +1 @@ +create index sessions_exp on sessions (expires); \ No newline at end of file diff --git a/geoMap.go b/geoMap.go index 9230853..7c944ea 100644 --- a/geoMap.go +++ b/geoMap.go @@ -4,7 +4,6 @@ import ( "embed" "encoding/json" "io" - "log" "net/http" "net/http/httptest" "net/http/httputil" @@ -82,7 +81,6 @@ var leafletFiles embed.FS func (a *goBlog) serveLeaflet(basePath string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { fileName := strings.TrimPrefix(r.URL.Path, basePath) - log.Println(basePath, fileName) fb, err := leafletFiles.ReadFile(fileName) if err != nil { a.serve404(w, r) diff --git a/go.mod b/go.mod index 963b4e7..efa2d8a 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,6 @@ require ( github.com/go-chi/chi/v5 v5.0.3 github.com/go-fed/httpsig v1.1.0 github.com/go-sql-driver/mysql v1.5.0 // indirect - github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f // indirect github.com/golang/glog v0.0.0-20210429001901-424d2337a529 // indirect github.com/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe // indirect github.com/gorilla/handlers v1.5.1 @@ -38,7 +37,6 @@ require ( github.com/lopezator/migrator v0.3.0 github.com/mattn/go-sqlite3 v1.14.8 github.com/microcosm-cc/bluemonday v1.0.15 - github.com/mitchellh/go-server-timing v1.0.1 github.com/paulmach/go.geojson v1.4.0 github.com/pquerna/otp v1.3.0 github.com/schollz/sqlite3dump v1.3.0 diff --git a/go.sum b/go.sum index dabd7b2..4ea8b9d 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -68,7 +67,6 @@ github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqO github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2 h1:t8KYCwSKsOEZBFELI4Pn/phbp38iJ1RRAkDFNin1aak= github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -108,14 +106,11 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/felixge/httpsnoop v1.0.0/go.mod h1:3+D9sFq0ahK/JeJPhCBUV1xlf4/eIYrUQaxulT0VzX8= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/gchaincl/sqlhooks/v2 v2.0.1 h1:j9WZAq1Tx/xngDfEdsgUww+o9iY3rLOYGYhYWcFxdmI= github.com/gchaincl/sqlhooks/v2 v2.0.1/go.mod h1:Qv7HXjGB9TehamVK52yW5H+a0RRhprIj3ESTcWOG2jw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -129,19 +124,14 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/gddo v0.0.0-20180823221919-9d8ff1c67be5/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4= -github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f h1:16RtHeWGkJMc80Etb8RPCcKevXGldr57+LOyZt8zOlg= -github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f/go.mod h1:ijRvpgDJDI262hYq/IQVYgf8hd8IHUs93Ol0kvMBAx4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20210429001901-424d2337a529 h1:2voWjNECnrZRbfwXxHB1/j8wa6xdKn85B5NzgVL/pTU= github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -167,10 +157,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -200,7 +188,6 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -214,7 +201,6 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -231,7 +217,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -240,7 +225,6 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jlaffaye/ftp v0.0.0-20210307004419-5d4190119067 h1:P2S26PMwXl8+ZGuOG3C69LG4be5vHafUayZm9VPw3tU= @@ -262,7 +246,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -283,13 +266,10 @@ github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lopezator/migrator v0.3.0 h1:VW/rR+J8NYwPdkBxjrFdjwejpgvP59LbmANJxXuNbuk= github.com/lopezator/migrator v0.3.0/go.mod h1:bpVAVPkWSvTw8ya2Pk7E/KiNAyDWNImgivQY79o8/8I= -github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= @@ -302,13 +282,10 @@ github.com/microcosm-cc/bluemonday v1.0.15/go.mod h1:ZLvAzeakRwrGnzQEvstVzVt3Zpq github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-server-timing v1.0.1 h1:f00/aIe8T3MrnLhQHu3tSWvnwc5GV/p5eutuu3hF/tE= -github.com/mitchellh/go-server-timing v1.0.1/go.mod h1:Mo6GKi9FSLwWFAMn3bqVPWe20y5ri5QGQuO9D9MCOxk= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -319,7 +296,6 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/paulmach/go.geojson v1.4.0 h1:5x5moCkCtDo5x8af62P9IOAYGQcYHtxz2QJ3x1DoCgY= github.com/paulmach/go.geojson v1.4.0/go.mod h1:YaKx1hKpWF+T2oj2lFJPsW/t1Q5e1jQI61eoQSTwpIs= -github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -349,19 +325,14 @@ github.com/snabb/diagio v1.0.0 h1:kovhQ1rDXoEbmpf/T5N2sUp2iOdxEg+TcqzbYVHV2V0= github.com/snabb/diagio v1.0.0/go.mod h1:ZyGaWFhfBVqstGUw6laYetzeTwZ2xxVPqTALx1QQa1w= github.com/snabb/sitemap v1.0.0 h1:7vJeNPAaaj7fQSRS3WYuJHzUjdnhLdSLLpvVtnhbzC0= github.com/snabb/sitemap v1.0.0/go.mod h1:Id8uz1+WYdiNmSjEi4BIvL5UwNPYLsTHzRbjmDwNDzA= -github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -496,7 +467,6 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210716203947-853a461950ff h1:j2EK/QoxYNBsXI4R7fQkkRUk8y6wnOBI+6hgPdP/6Ds= golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -509,7 +479,6 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -579,7 +548,6 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -638,7 +606,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -668,7 +635,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -710,7 +676,6 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -745,7 +710,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= diff --git a/http.go b/http.go index 2e0bb24..ce80c7f 100644 --- a/http.go +++ b/http.go @@ -17,7 +17,6 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/justinas/alice" - servertiming "github.com/mitchellh/go-server-timing" "golang.org/x/crypto/acme" "golang.org/x/crypto/acme/autocert" "golang.org/x/net/context" @@ -38,17 +37,15 @@ func (a *goBlog) startServer() (err error) { } a.d = fixHTTPHandler(router) // Set basic middlewares - var finalHandler http.Handler = a.d - if a.cfg.Server.PublicHTTPS || a.cfg.Server.SecurityHeaders { - finalHandler = a.securityHeaders(finalHandler) - } - finalHandler = servertiming.Middleware(finalHandler, nil) - finalHandler = middleware.Heartbeat("/ping")(finalHandler) - finalHandler = middleware.Compress(flate.DefaultCompression)(finalHandler) - finalHandler = middleware.Recoverer(finalHandler) + h := alice.New() if a.cfg.Server.Logging { - finalHandler = a.logMiddleware(finalHandler) + h = h.Append(a.logMiddleware) } + h = h.Append(middleware.Recoverer, middleware.Compress(flate.DefaultCompression), middleware.Heartbeat("/ping")) + if a.cfg.Server.PublicHTTPS || a.cfg.Server.SecurityHeaders { + h = h.Append(a.securityHeaders) + } + finalHandler := h.Then(a.d) // Start Onion service if a.cfg.Server.Tor { go func() { @@ -154,10 +151,9 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { r.Use(noIndexHeader) } - // Login middleware etc. + // Login and captcha middleware r.Use(a.checkIsLogin) r.Use(a.checkIsCaptcha) - r.Use(a.checkLoggedIn) // Logout r.With(a.authMiddleware).Get("/login", serveLogin) @@ -187,7 +183,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { r.Post("/{blog}/inbox", a.apHandleInbox) }) r.Group(func(r chi.Router) { - r.Use(a.cache.cacheMiddleware) + r.Use(a.cacheMiddleware) r.Get("/.well-known/webfinger", a.apHandleWebfinger) r.Get("/.well-known/host-meta", handleWellKnownHostMeta) r.Get("/.well-known/nodeinfo", a.serveNodeInfoDiscover) @@ -236,7 +232,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { r.Handle("/captcha/*", captcha.Server(500, 250)) // Short paths - r.With(privateModeHandler...).With(a.cache.cacheMiddleware).Get("/s/{id:[0-9a-fA-F]+}", a.redirectToLongPath) + r.With(privateModeHandler...).With(a.cacheMiddleware).Get("/s/{id:[0-9a-fA-F]+}", a.redirectToLongPath) for blog, blogConfig := range a.cfg.Blogs { sbm := middleware.WithValue(blogContextKey, blog) @@ -244,7 +240,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { // Sections r.Group(func(r chi.Router) { r.Use(privateModeHandler...) - r.Use(a.cache.cacheMiddleware, sbm) + r.Use(a.cacheMiddleware, sbm) for _, section := range blogConfig.Sections { if section.Name != "" { r.Group(func(r chi.Router) { @@ -264,7 +260,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { // Taxonomies r.Group(func(r chi.Router) { r.Use(privateModeHandler...) - r.Use(a.cache.cacheMiddleware, sbm) + r.Use(a.cacheMiddleware, sbm) for _, taxonomy := range blogConfig.Taxonomies { if taxonomy.Name != "" { r.Group(func(r chi.Router) { @@ -285,7 +281,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { r.Group(func(r chi.Router) { photoPath := blogConfig.getRelativePath(defaultIfEmpty(pc.Path, defaultPhotosPath)) r.Use(privateModeHandler...) - r.Use(a.cache.cacheMiddleware, sbm, middleware.WithValue(indexConfigKey, &indexConfig{ + r.Use(a.cacheMiddleware, sbm, middleware.WithValue(indexConfigKey, &indexConfig{ path: photoPath, parameter: pc.Parameter, title: pc.Title, @@ -308,7 +304,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { )) r.Group(func(r chi.Router) { r.Use(privateModeHandler...) - r.Use(a.cache.cacheMiddleware) + r.Use(a.cacheMiddleware) r.Get("/", a.serveSearch) r.Post("/", a.serveSearch) searchResultPath := "/" + searchPlaceholder @@ -316,7 +312,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { r.Get(searchResultPath+feedPath, a.serveSearchResult) r.Get(searchResultPath+paginationPath, a.serveSearchResult) }) - r.With(a.cache.cacheMiddleware).Get("/opensearch.xml", a.serveOpenSearch) + r.With(a.cacheMiddleware).Get("/opensearch.xml", a.serveOpenSearch) }) } @@ -325,7 +321,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { statsPath := blogConfig.getRelativePath(defaultIfEmpty(bsc.Path, defaultBlogStatsPath)) r.Group(func(r chi.Router) { r.Use(privateModeHandler...) - r.Use(a.cache.cacheMiddleware, sbm) + r.Use(a.cacheMiddleware, sbm) r.Get(statsPath, a.serveBlogStats) r.Get(statsPath+".table.html", a.serveBlogStatsTable) }) @@ -334,7 +330,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { // Date archives r.Group(func(r chi.Router) { r.Use(privateModeHandler...) - r.Use(a.cache.cacheMiddleware, sbm) + r.Use(a.cacheMiddleware, sbm) yearRegex := `/{year:x|\d\d\d\d}` monthRegex := `/{month:x|\d\d}` @@ -361,9 +357,9 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { r.Group(func(r chi.Router) { r.Use(privateModeHandler...) r.Use(sbm) - r.With(a.checkActivityStreamsRequest, a.cache.cacheMiddleware).Get(blogConfig.getRelativePath(""), a.serveHome) - r.With(a.cache.cacheMiddleware).Get(blogConfig.getRelativePath("")+feedPath, a.serveHome) - r.With(a.cache.cacheMiddleware).Get(blogConfig.getRelativePath(paginationPath), a.serveHome) + r.With(a.checkActivityStreamsRequest, a.cacheMiddleware).Get(blogConfig.getRelativePath(""), a.serveHome) + r.With(a.cacheMiddleware).Get(blogConfig.getRelativePath("")+feedPath, a.serveHome) + r.With(a.cacheMiddleware).Get(blogConfig.getRelativePath(paginationPath), a.serveHome) }) } @@ -371,7 +367,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { for _, cp := range blogConfig.CustomPages { scp := middleware.WithValue(customPageContextKey, cp) if cp.Cache { - r.With(privateModeHandler...).With(a.cache.cacheMiddleware, sbm, scp).Get(cp.Path, a.serveCustomPage) + r.With(privateModeHandler...).With(a.cacheMiddleware, sbm, scp).Get(cp.Path, a.serveCustomPage) } else { r.With(privateModeHandler...).With(sbm, scp).Get(cp.Path, a.serveCustomPage) } @@ -407,7 +403,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { r.Route(commentsPath, func(r chi.Router) { r.Use(sbm, middleware.WithValue(pathContextKey, commentsPath)) r.Use(privateModeHandler...) - r.With(a.cache.cacheMiddleware, noIndexHeader).Get("/{id:[0-9]+}", a.serveComment) + r.With(a.cacheMiddleware, noIndexHeader).Get("/{id:[0-9]+}", a.serveComment) r.With(a.captchaMiddleware).Post("/", a.createComment) r.Group(func(r chi.Router) { // Admin @@ -424,7 +420,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { brPath := blogConfig.getRelativePath(defaultIfEmpty(brConfig.Path, defaultBlogrollPath)) r.Group(func(r chi.Router) { r.Use(privateModeHandler...) - r.Use(a.cache.cacheMiddleware, sbm) + r.Use(a.cacheMiddleware, sbm) r.Get(brPath, a.serveBlogroll) r.Get(brPath+".opml", a.serveBlogrollExport) }) @@ -436,7 +432,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { r.Route(mapPath, func(r chi.Router) { r.Use(privateModeHandler...) r.Group(func(r chi.Router) { - r.Use(a.cache.cacheMiddleware, sbm) + r.Use(a.cacheMiddleware, sbm) r.Get("/", a.serveGeoMap) r.HandleFunc("/leaflet/*", a.serveLeaflet(mapPath+"/")) }) @@ -449,7 +445,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { contactPath := blogConfig.getRelativePath(defaultIfEmpty(cc.Path, defaultContactPath)) r.Route(contactPath, func(r chi.Router) { r.Use(privateModeHandler...) - r.Use(a.cache.cacheMiddleware, sbm) + r.Use(a.cacheMiddleware, sbm) r.Get("/", a.serveContactForm) r.With(a.captchaMiddleware).Post("/", a.sendContactSubmission) }) @@ -458,7 +454,7 @@ func (a *goBlog) buildRouter() (*chi.Mux, error) { } // Sitemap - r.With(privateModeHandler...).With(a.cache.cacheMiddleware).Get(sitemapPath, a.serveSitemap) + r.With(privateModeHandler...).With(a.cacheMiddleware).Get(sitemapPath, a.serveSitemap) // Robots.txt - doesn't need cache, because it's too simple if !privateMode { @@ -517,7 +513,7 @@ func (a *goBlog) servePostsAliasesRedirects(pmh ...func(http.Handler) http.Handl // Is post, check status switch postStatus(value) { case statusPublished, statusUnlisted: - alicePrivate.Append(a.checkActivityStreamsRequest, a.cache.cacheMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r) + alicePrivate.Append(a.checkActivityStreamsRequest, a.cacheMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r) return case statusDraft, statusPrivate: alice.New(a.authMiddleware).ThenFunc(a.servePost).ServeHTTP(w, r) @@ -525,20 +521,20 @@ func (a *goBlog) servePostsAliasesRedirects(pmh ...func(http.Handler) http.Handl } case "alias": // Is alias, redirect - alicePrivate.Append(a.cache.cacheMiddleware).ThenFunc(func(w http.ResponseWriter, r *http.Request) { + alicePrivate.Append(a.cacheMiddleware).ThenFunc(func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, value, http.StatusFound) }).ServeHTTP(w, r) return case "deleted": // Is deleted, serve 410 - alicePrivate.Append(a.cache.cacheMiddleware).ThenFunc(func(w http.ResponseWriter, r *http.Request) { + alicePrivate.Append(a.cacheMiddleware).ThenFunc(func(w http.ResponseWriter, r *http.Request) { a.serve410(w, r) }).ServeHTTP(w, r) return } } // No post, check regex redirects or serve 404 error - alice.New(a.cache.cacheMiddleware, a.checkRegexRedirects).ThenFunc(a.serve404).ServeHTTP(w, r) + alice.New(a.cacheMiddleware, a.checkRegexRedirects).ThenFunc(a.serve404).ServeHTTP(w, r) } } diff --git a/posts.go b/posts.go index 3acc724..1cf7da6 100644 --- a/posts.go +++ b/posts.go @@ -13,7 +13,6 @@ import ( "github.com/go-chi/chi/v5" "github.com/microcosm-cc/bluemonday" - servertiming "github.com/mitchellh/go-server-timing" "github.com/vcraescu/go-paginator" ) @@ -46,9 +45,7 @@ const ( ) func (a *goBlog) servePost(w http.ResponseWriter, r *http.Request) { - t := servertiming.FromContext(r.Context()).NewMetric("gp").Start() p, err := a.db.getPost(r.URL.Path) - t.Stop() if err == errPostNotFound { a.serve404(w, r) return @@ -253,9 +250,7 @@ func (a *goBlog) serveIndex(w http.ResponseWriter, r *http.Request) { }, db: a.db}, a.cfg.Blogs[blog].Pagination) p.SetPage(pageNo) var posts []*post - t := servertiming.FromContext(r.Context()).NewMetric("gp").Start() err := p.Results(&posts) - t.Stop() if err != nil { a.serveError(w, r, err.Error(), http.StatusInternalServerError) return diff --git a/render.go b/render.go index 8d3e5fd..e652995 100644 --- a/render.go +++ b/render.go @@ -11,7 +11,6 @@ import ( "path/filepath" "strings" - servertiming "github.com/mitchellh/go-server-timing" "go.goblog.app/app/pkgs/contenttype" ) @@ -115,9 +114,6 @@ func (a *goBlog) render(w http.ResponseWriter, r *http.Request, template string, } func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, statusCode int, template string, data *renderData) { - // Server timing - t := servertiming.FromContext(r.Context()).NewMetric("r").Start() - defer t.Stop() // Check render data a.checkRenderData(r, data) // Set content type @@ -165,7 +161,7 @@ func (a *goBlog) checkRenderData(r *http.Request, data *renderData) { data.TorUsed = true } // Check login - if loggedIn, ok := r.Context().Value(loggedInKey).(bool); ok && loggedIn { + if a.isLoggedIn(r) { data.LoggedIn = true } // Check if comments enabled diff --git a/sessions.go b/sessions.go index 3777ed2..bf8e523 100644 --- a/sessions.go +++ b/sessions.go @@ -2,7 +2,6 @@ package main import ( "database/sql" - "errors" "fmt" "log" "net/http" @@ -31,7 +30,7 @@ func (a *goBlog) initSessions() { deleteExpiredSessions() a.hourlyHooks = append(a.hourlyHooks, deleteExpiredSessions) a.loginSessions = &dbSessionStore{ - codecs: securecookie.CodecsFromPairs(a.jwtKey()), + codecs: securecookie.CodecsFromPairs([]byte(a.cfg.Server.JWTSecret)), options: &sessions.Options{ Secure: a.httpsConfigured(), HttpOnly: true, @@ -42,7 +41,7 @@ func (a *goBlog) initSessions() { db: a.db, } a.captchaSessions = &dbSessionStore{ - codecs: securecookie.CodecsFromPairs(a.jwtKey()), + codecs: securecookie.CodecsFromPairs([]byte(a.cfg.Server.JWTSecret)), options: &sessions.Options{ Secure: a.httpsConfigured(), HttpOnly: true, @@ -77,25 +76,19 @@ func (s *dbSessionStore) New(r *http.Request, name string) (session *sessions.Se return session, err } -func (s *dbSessionStore) Save(r *http.Request, w http.ResponseWriter, ss *sessions.Session) error { - _, err := s.SaveGetCookie(r, w, ss) - return err -} - -func (s *dbSessionStore) SaveGetCookie(r *http.Request, w http.ResponseWriter, ss *sessions.Session) (cookie *http.Cookie, err error) { +func (s *dbSessionStore) Save(r *http.Request, w http.ResponseWriter, ss *sessions.Session) (err error) { if ss.ID == "" { if err = s.insert(ss); err != nil { - return nil, err + return err } } else if err = s.save(ss); err != nil { - return nil, err + return err } if encoded, err := securecookie.EncodeMulti(ss.Name(), ss.ID, s.codecs...); err != nil { - return nil, err + return err } else { - cookie = sessions.NewCookie(ss.Name(), encoded, ss.Options) - http.SetCookie(w, cookie) - return cookie, nil + http.SetCookie(w, sessions.NewCookie(ss.Name(), encoded, ss.Options)) + return nil } } @@ -113,22 +106,17 @@ func (s *dbSessionStore) Delete(r *http.Request, w http.ResponseWriter, session } func (s *dbSessionStore) load(session *sessions.Session) (err error) { - row, err := s.db.queryRow("select data, created, modified, expires from sessions where id = @id", sql.Named("id", session.ID)) + row, err := s.db.queryRow("select data, created, modified, expires from sessions where id = @id and expires > @now", sql.Named("id", session.ID), sql.Named("now", utcNowString())) if err != nil { return err } var data, createdStr, modifiedStr, expiresStr string - if err = row.Scan(&data, &createdStr, &modifiedStr, &expiresStr); err == sql.ErrNoRows { - return nil - } else if err != nil { + if err = row.Scan(&data, &createdStr, &modifiedStr, &expiresStr); err != nil { return err } created, _ := dateparse.ParseLocal(createdStr) modified, _ := dateparse.ParseLocal(modifiedStr) expires, _ := dateparse.ParseLocal(expiresStr) - if expires.Before(time.Now()) { - return errors.New("session expired") - } if err = securecookie.DecodeMulti(session.Name(), data, &session.Values, s.codecs...); err != nil { return err } @@ -149,7 +137,7 @@ func (s *dbSessionStore) insert(session *sessions.Session) (err error) { if err != nil { return err } - res, err := s.db.exec("insert into sessions(data, created, modified, expires) values(@data, @created, @modified, @expires)", + res, err := s.db.exec("insert or replace into sessions(data, created, modified, expires) values(@data, @created, @modified, @expires)", sql.Named("data", encoded), sql.Named("created", created.Format(time.RFC3339)), sql.Named("modified", modified.Format(time.RFC3339)), sql.Named("expires", expires.Format(time.RFC3339))) if err != nil { return err diff --git a/webmentionVerification.go b/webmentionVerification.go index 86838f7..03a8f17 100644 --- a/webmentionVerification.go +++ b/webmentionVerification.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "context" "database/sql" "encoding/gob" "errors" @@ -86,7 +85,8 @@ func (a *goBlog) verifyMention(m *mention) error { // Server not yet started time.Sleep(1 * time.Second) } - a.d.ServeHTTP(rec, req.WithContext(context.WithValue(req.Context(), loggedInKey, true))) + setLoggedIn(req) + a.d.ServeHTTP(rec, req) resp = rec.Result() } else { req.Header.Set(userAgent, appUserAgent)