mirror of https://github.com/jlelse/GoBlog
Use sessions instead of jwt
This commit is contained in:
parent
d7da17bda1
commit
1b2eed9897
|
@ -7,9 +7,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/dgrijalva/jwt-go"
|
|
||||||
"github.com/pquerna/otp/totp"
|
"github.com/pquerna/otp/totp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,10 +17,6 @@ func checkCredentials(username, password, totpPasscode string) bool {
|
||||||
(appConfig.User.TOTP == "" || totp.Validate(totpPasscode, appConfig.User.TOTP))
|
(appConfig.User.TOTP == "" || totp.Validate(totpPasscode, appConfig.User.TOTP))
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkUsernameTOTP(username string, totp bool) bool {
|
|
||||||
return username == appConfig.User.Nick && totp == (appConfig.User.TOTP != "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAppPasswords(username, password string) bool {
|
func checkAppPasswords(username, password string) bool {
|
||||||
for _, apw := range appConfig.User.AppPasswords {
|
for _, apw := range appConfig.User.AppPasswords {
|
||||||
if apw.Username == username && apw.Password == password {
|
if apw.Username == username && apw.Password == password {
|
||||||
|
@ -38,22 +32,22 @@ func jwtKey() []byte {
|
||||||
|
|
||||||
func authMiddleware(next http.Handler) http.Handler {
|
func authMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Check if already logged in
|
// 1. Check if already logged in
|
||||||
if loggedIn, ok := r.Context().Value(loggedInKey).(bool); ok && loggedIn {
|
if loggedIn, ok := r.Context().Value(loggedInKey).(bool); ok && loggedIn {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 1. Check BasicAuth (just for app passwords)
|
// 2. Check BasicAuth (just for app passwords)
|
||||||
if username, password, ok := r.BasicAuth(); ok && checkAppPasswords(username, password) {
|
if username, password, ok := r.BasicAuth(); ok && checkAppPasswords(username, password) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 2. Check JWT
|
// 3. Check login cookie
|
||||||
if checkAuthToken(r) {
|
if checkLoginCookie(r) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), loggedInKey, true)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 3. Show login form
|
// 4. Show login form
|
||||||
w.Header().Set("Cache-Control", "no-store,max-age=0")
|
w.Header().Set("Cache-Control", "no-store,max-age=0")
|
||||||
h, _ := json.Marshal(r.Header.Clone())
|
h, _ := json.Marshal(r.Header.Clone())
|
||||||
b, _ := io.ReadAll(io.LimitReader(r.Body, 2000000)) // Only allow 20 Megabyte
|
b, _ := io.ReadAll(io.LimitReader(r.Body, 2000000)) // Only allow 20 Megabyte
|
||||||
|
@ -74,25 +68,11 @@ func authMiddleware(next http.Handler) http.Handler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAuthToken(r *http.Request) bool {
|
|
||||||
if tokenCookie, err := r.Cookie("token"); err == nil {
|
|
||||||
claims := &authClaims{}
|
|
||||||
if tkn, err := jwt.ParseWithClaims(tokenCookie.Value, claims, func(t *jwt.Token) (interface{}, error) {
|
|
||||||
return jwtKey(), nil
|
|
||||||
}); err == nil && tkn.Valid &&
|
|
||||||
claims.TokenType == "login" &&
|
|
||||||
checkUsernameTOTP(claims.Username, claims.TOTP) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const loggedInKey requestContextKey = "loggedIn"
|
const loggedInKey requestContextKey = "loggedIn"
|
||||||
|
|
||||||
func checkLoggedIn(next http.Handler) http.Handler {
|
func checkLoggedIn(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
if checkAuthToken(r) {
|
if checkLoginCookie(r) {
|
||||||
next.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), loggedInKey, true)))
|
next.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), loggedInKey, true)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -100,6 +80,16 @@ func checkLoggedIn(next http.Handler) http.Handler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkLoginCookie(r *http.Request) bool {
|
||||||
|
ses, err := loginSessionsStore.Get(r, "l")
|
||||||
|
if err == nil && ses != nil {
|
||||||
|
if login, ok := ses.Values["login"]; ok && login.(bool) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func checkIsLogin(next http.Handler) http.Handler {
|
func checkIsLogin(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
if !checkLogin(rw, r) {
|
if !checkLogin(rw, r) {
|
||||||
|
@ -134,58 +124,31 @@ func checkLogin(w http.ResponseWriter, r *http.Request) bool {
|
||||||
req.Header[k] = v
|
req.Header[k] = v
|
||||||
}
|
}
|
||||||
// Cookie
|
// Cookie
|
||||||
tokenCookie, err := createTokenCookie()
|
ses, err := loginSessionsStore.Get(r, "l")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
req.AddCookie(tokenCookie)
|
ses.Values["login"] = true
|
||||||
http.SetCookie(w, tokenCookie)
|
cookie, err := loginSessionsStore.SaveGetCookie(r, w, ses)
|
||||||
|
if err != nil {
|
||||||
|
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
req.AddCookie(cookie)
|
||||||
// Serve original request
|
// Serve original request
|
||||||
d.ServeHTTP(w, req)
|
d.ServeHTTP(w, req)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
type authClaims struct {
|
|
||||||
*jwt.StandardClaims
|
|
||||||
TokenType string
|
|
||||||
Username string
|
|
||||||
TOTP bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTokenCookie() (*http.Cookie, error) {
|
|
||||||
expiration := time.Now().Add(7 * 24 * time.Hour)
|
|
||||||
tokenString, err := jwt.NewWithClaims(jwt.SigningMethodHS256, &authClaims{
|
|
||||||
&jwt.StandardClaims{ExpiresAt: expiration.Unix()},
|
|
||||||
"login",
|
|
||||||
appConfig.User.Nick,
|
|
||||||
appConfig.User.TOTP != "",
|
|
||||||
}).SignedString(jwtKey())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &http.Cookie{
|
|
||||||
Name: "token",
|
|
||||||
Value: tokenString,
|
|
||||||
Expires: expiration,
|
|
||||||
Secure: httpsConfigured(),
|
|
||||||
HttpOnly: true,
|
|
||||||
SameSite: http.SameSiteLaxMode,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to set auth middleware!
|
// Need to set auth middleware!
|
||||||
func serveLogin(w http.ResponseWriter, r *http.Request) {
|
func serveLogin(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, "/", http.StatusFound)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveLogout(w http.ResponseWriter, r *http.Request) {
|
func serveLogout(w http.ResponseWriter, r *http.Request) {
|
||||||
http.SetCookie(w, &http.Cookie{
|
if ses, err := loginSessionsStore.Get(r, "l"); err == nil && ses != nil {
|
||||||
Name: "token",
|
_ = loginSessionsStore.Delete(r, w, ses)
|
||||||
MaxAge: -1,
|
}
|
||||||
Secure: httpsConfigured(),
|
|
||||||
HttpOnly: true,
|
|
||||||
SameSite: http.SameSiteLaxMode,
|
|
||||||
})
|
|
||||||
http.Redirect(w, r, "/", http.StatusFound)
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
100
captcha.go
100
captcha.go
|
@ -6,20 +6,16 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/dchest/captcha"
|
"github.com/dchest/captcha"
|
||||||
"github.com/dgrijalva/jwt-go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func captchaMiddleware(next http.Handler) http.Handler {
|
func captchaMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// 1. Check JWT
|
// 1. Check Cookie
|
||||||
claims := &captchaClaims{}
|
ses, err := captchaSessionsStore.Get(r, "c")
|
||||||
if captchaCookie, err := r.Cookie("captcha"); err == nil {
|
if err == nil && ses != nil {
|
||||||
if tkn, err := jwt.ParseWithClaims(captchaCookie.Value, claims, func(t *jwt.Token) (interface{}, error) {
|
if captcha, ok := ses.Values["captcha"]; ok && captcha.(bool) {
|
||||||
return jwtKey(), nil
|
|
||||||
}); err == nil && tkn.Valid && claims.TokenType == "captcha" {
|
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -54,59 +50,41 @@ func checkIsCaptcha(next http.Handler) http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkCaptcha(w http.ResponseWriter, r *http.Request) bool {
|
func checkCaptcha(w http.ResponseWriter, r *http.Request) bool {
|
||||||
if r.Method == http.MethodPost &&
|
if r.Method != http.MethodPost {
|
||||||
r.Header.Get(contentType) == contentTypeWWWForm &&
|
return false
|
||||||
r.FormValue("captchaaction") == "captcha" {
|
|
||||||
// Do original request
|
|
||||||
captchabody, _ := base64.StdEncoding.DecodeString(r.FormValue("captchabody"))
|
|
||||||
req, _ := http.NewRequest(r.FormValue("captchamethod"), r.RequestURI, bytes.NewReader(captchabody))
|
|
||||||
// Copy original headers
|
|
||||||
captchaheaders, _ := base64.StdEncoding.DecodeString(r.FormValue("captchaheaders"))
|
|
||||||
var headers http.Header
|
|
||||||
_ = json.Unmarshal(captchaheaders, &headers)
|
|
||||||
for k, v := range headers {
|
|
||||||
req.Header[k] = v
|
|
||||||
}
|
|
||||||
// Check captcha
|
|
||||||
if captcha.VerifyString(r.FormValue("captchaid"), r.FormValue("digits")) {
|
|
||||||
// Create cookie
|
|
||||||
captchaCookie, err := createCaptchaCookie()
|
|
||||||
if err != nil {
|
|
||||||
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// Add cookie to original request
|
|
||||||
req.AddCookie(captchaCookie)
|
|
||||||
// Send cookie
|
|
||||||
http.SetCookie(w, captchaCookie)
|
|
||||||
}
|
|
||||||
// Serve original request
|
|
||||||
d.ServeHTTP(w, req)
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
return false
|
if r.Header.Get(contentType) != contentTypeWWWForm {
|
||||||
}
|
return false
|
||||||
|
|
||||||
type captchaClaims struct {
|
|
||||||
*jwt.StandardClaims
|
|
||||||
TokenType string
|
|
||||||
}
|
|
||||||
|
|
||||||
func createCaptchaCookie() (*http.Cookie, error) {
|
|
||||||
expiration := time.Now().Add(24 * time.Hour)
|
|
||||||
tokenString, err := jwt.NewWithClaims(jwt.SigningMethodHS256, &captchaClaims{
|
|
||||||
&jwt.StandardClaims{ExpiresAt: expiration.Unix()},
|
|
||||||
"captcha",
|
|
||||||
}).SignedString(jwtKey())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
return &http.Cookie{
|
if r.FormValue("captchaaction") != "captcha" {
|
||||||
Name: "captcha",
|
return false
|
||||||
Value: tokenString,
|
}
|
||||||
Expires: expiration,
|
// Prepare original request
|
||||||
Secure: httpsConfigured(),
|
captchabody, _ := base64.StdEncoding.DecodeString(r.FormValue("captchabody"))
|
||||||
HttpOnly: true,
|
req, _ := http.NewRequest(r.FormValue("captchamethod"), r.RequestURI, bytes.NewReader(captchabody))
|
||||||
SameSite: http.SameSiteLaxMode,
|
// Copy original headers
|
||||||
}, nil
|
captchaheaders, _ := base64.StdEncoding.DecodeString(r.FormValue("captchaheaders"))
|
||||||
|
var headers http.Header
|
||||||
|
_ = json.Unmarshal(captchaheaders, &headers)
|
||||||
|
for k, v := range headers {
|
||||||
|
req.Header[k] = v
|
||||||
|
}
|
||||||
|
// Check captcha and create cookie
|
||||||
|
if captcha.VerifyString(r.FormValue("captchaid"), r.FormValue("digits")) {
|
||||||
|
ses, err := captchaSessionsStore.Get(r, "c")
|
||||||
|
if err != nil {
|
||||||
|
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ses.Values["captcha"] = true
|
||||||
|
cookie, err := captchaSessionsStore.SaveGetCookie(r, w, ses)
|
||||||
|
if err != nil {
|
||||||
|
serveError(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
req.AddCookie(cookie)
|
||||||
|
}
|
||||||
|
// Serve original request
|
||||||
|
d.ServeHTTP(w, req)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,6 +148,15 @@ func migrateDb() error {
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
&migrator.Migration{
|
||||||
|
Name: "00013",
|
||||||
|
Func: func(tx *sql.Tx) error {
|
||||||
|
_, err := tx.Exec(`
|
||||||
|
create table sessions (id integer primary key autoincrement, data text default '', created text default '', modified datetime default '', expires text default '');
|
||||||
|
`)
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
9
go.mod
9
go.mod
|
@ -11,7 +11,6 @@ require (
|
||||||
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca
|
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca
|
||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||||
github.com/dgraph-io/ristretto v0.0.4-0.20210504190834-0bf2acd73aa3
|
github.com/dgraph-io/ristretto v0.0.4-0.20210504190834-0bf2acd73aa3
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
|
||||||
github.com/elnormous/contenttype v1.0.0
|
github.com/elnormous/contenttype v1.0.0
|
||||||
github.com/felixge/httpsnoop v1.0.2 // indirect
|
github.com/felixge/httpsnoop v1.0.2 // indirect
|
||||||
github.com/go-chi/chi/v5 v5.0.3
|
github.com/go-chi/chi/v5 v5.0.3
|
||||||
|
@ -24,6 +23,8 @@ require (
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe // indirect
|
github.com/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe // indirect
|
||||||
github.com/gorilla/feeds v1.1.1
|
github.com/gorilla/feeds v1.1.1
|
||||||
github.com/gorilla/handlers v1.5.1
|
github.com/gorilla/handlers v1.5.1
|
||||||
|
github.com/gorilla/securecookie v1.1.1
|
||||||
|
github.com/gorilla/sessions v1.2.1
|
||||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||||
github.com/joncrlsn/dque v0.0.0-20200702023911-3e80e3146ce5
|
github.com/joncrlsn/dque v0.0.0-20200702023911-3e80e3146ce5
|
||||||
github.com/kaorimatz/go-opml v0.0.0-20210201121027-bc8e2852d7f9
|
github.com/kaorimatz/go-opml v0.0.0-20210201121027-bc8e2852d7f9
|
||||||
|
@ -39,7 +40,7 @@ require (
|
||||||
github.com/miekg/dns v1.1.42 // indirect
|
github.com/miekg/dns v1.1.42 // indirect
|
||||||
github.com/mitchellh/go-server-timing v1.0.1
|
github.com/mitchellh/go-server-timing v1.0.1
|
||||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.0 // indirect
|
github.com/pelletier/go-toml v1.9.1 // indirect
|
||||||
github.com/pquerna/otp v1.3.0
|
github.com/pquerna/otp v1.3.0
|
||||||
github.com/schollz/sqlite3dump v1.2.4
|
github.com/schollz/sqlite3dump v1.2.4
|
||||||
github.com/smartystreets/assertions v1.2.0 // indirect
|
github.com/smartystreets/assertions v1.2.0 // indirect
|
||||||
|
@ -57,12 +58,12 @@ require (
|
||||||
github.com/yuin/goldmark-emoji v1.0.1
|
github.com/yuin/goldmark-emoji v1.0.1
|
||||||
go.uber.org/multierr v1.7.0 // indirect
|
go.uber.org/multierr v1.7.0 // indirect
|
||||||
go.uber.org/zap v1.16.0 // indirect
|
go.uber.org/zap v1.16.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf // indirect
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect
|
||||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
|
||||||
golang.org/x/mod v0.4.1 // indirect
|
golang.org/x/mod v0.4.1 // indirect
|
||||||
golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
golang.org/x/net v0.0.0-20210510120150-4163338589ed
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
|
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 // indirect
|
||||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||||
|
|
17
go.sum
17
go.sum
|
@ -63,7 +63,6 @@ github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ
|
||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
|
||||||
github.com/dgraph-io/ristretto v0.0.4-0.20210504190834-0bf2acd73aa3 h1:jU/wpYsEL+8JPLf/QcjkQKI5g0dOjSuwcMjkThxt5x0=
|
github.com/dgraph-io/ristretto v0.0.4-0.20210504190834-0bf2acd73aa3 h1:jU/wpYsEL+8JPLf/QcjkQKI5g0dOjSuwcMjkThxt5x0=
|
||||||
github.com/dgraph-io/ristretto v0.0.4-0.20210504190834-0bf2acd73aa3/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
|
github.com/dgraph-io/ristretto v0.0.4-0.20210504190834-0bf2acd73aa3/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||||
|
@ -141,6 +140,10 @@ github.com/gorilla/feeds v1.1.1 h1:HwKXxqzcRNg9to+BbvJog4+f3s/xzvtZXICcQGutYfY=
|
||||||
github.com/gorilla/feeds v1.1.1/go.mod h1:Nk0jZrvPFZX1OBe5NPiddPw7CfwF6Q9eqzaBbaightA=
|
github.com/gorilla/feeds v1.1.1/go.mod h1:Nk0jZrvPFZX1OBe5NPiddPw7CfwF6Q9eqzaBbaightA=
|
||||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||||
|
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||||
|
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/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
@ -257,8 +260,8 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pelletier/go-toml v1.9.0 h1:NOd0BRdOKpPf0SxkL3HxSQOG7rNh+4kl6PHcBPFs7Q0=
|
github.com/pelletier/go-toml v1.9.1 h1:a6qW1EVNZWH9WGI6CsYdD8WAylkoXBS5yv0XHlh17Tc=
|
||||||
github.com/pelletier/go-toml v1.9.0/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.1/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
@ -371,8 +374,8 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
|
||||||
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
@ -458,8 +461,8 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
|
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
|
||||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
|
1
main.go
1
main.go
|
@ -136,6 +136,7 @@ func main() {
|
||||||
}
|
}
|
||||||
initTelegram()
|
initTelegram()
|
||||||
initBlogStats()
|
initBlogStats()
|
||||||
|
initSessions()
|
||||||
|
|
||||||
// Start cron hooks
|
// Start cron hooks
|
||||||
startHourlyHooks()
|
startHourlyHooks()
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/araddon/dateparse"
|
||||||
|
"github.com/gorilla/securecookie"
|
||||||
|
"github.com/gorilla/sessions"
|
||||||
|
)
|
||||||
|
|
||||||
|
var loginSessionsStore, captchaSessionsStore *dbSessionStore
|
||||||
|
|
||||||
|
const (
|
||||||
|
sessionCreatedOn = "created"
|
||||||
|
sessionModifiedOn = "modified"
|
||||||
|
sessionExpiresOn = "expires"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initSessions() {
|
||||||
|
deleteExpiredSessions := func() {
|
||||||
|
if _, err := appDbExec("delete from sessions where expires < @now",
|
||||||
|
sql.Named("now", time.Now().Local().String())); err != nil {
|
||||||
|
log.Println("Failed to delete expired sessions:", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deleteExpiredSessions()
|
||||||
|
hourlyHooks = append(hourlyHooks, deleteExpiredSessions)
|
||||||
|
loginSessionsStore = &dbSessionStore{
|
||||||
|
codecs: securecookie.CodecsFromPairs(jwtKey()),
|
||||||
|
options: &sessions.Options{
|
||||||
|
Secure: httpsConfigured(),
|
||||||
|
HttpOnly: true,
|
||||||
|
SameSite: http.SameSiteLaxMode,
|
||||||
|
MaxAge: int((7 * 24 * time.Hour).Seconds()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
captchaSessionsStore = &dbSessionStore{
|
||||||
|
codecs: securecookie.CodecsFromPairs(jwtKey()),
|
||||||
|
options: &sessions.Options{
|
||||||
|
Secure: httpsConfigured(),
|
||||||
|
HttpOnly: true,
|
||||||
|
SameSite: http.SameSiteLaxMode,
|
||||||
|
MaxAge: int((24 * time.Hour).Seconds()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type dbSessionStore struct {
|
||||||
|
options *sessions.Options
|
||||||
|
codecs []securecookie.Codec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dbSessionStore) Get(r *http.Request, name string) (*sessions.Session, error) {
|
||||||
|
return sessions.GetRegistry(r).Get(s, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dbSessionStore) New(r *http.Request, name string) (session *sessions.Session, err error) {
|
||||||
|
session = sessions.NewSession(s, name)
|
||||||
|
opts := *s.options
|
||||||
|
session.Options = &opts
|
||||||
|
session.IsNew = true
|
||||||
|
if cook, errCookie := r.Cookie(name); errCookie == nil {
|
||||||
|
if err = securecookie.DecodeMulti(name, cook.Value, &session.ID, s.codecs...); err == nil {
|
||||||
|
session.IsNew = s.load(session) == nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
if ss.ID == "" {
|
||||||
|
if err = s.insert(ss); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else if err = s.save(ss); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if encoded, err := securecookie.EncodeMulti(ss.Name(), ss.ID, s.codecs...); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
cookie = sessions.NewCookie(ss.Name(), encoded, ss.Options)
|
||||||
|
http.SetCookie(w, cookie)
|
||||||
|
return cookie, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dbSessionStore) Delete(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
|
||||||
|
options := *session.Options
|
||||||
|
options.MaxAge = -1
|
||||||
|
http.SetCookie(w, sessions.NewCookie(session.Name(), "", &options))
|
||||||
|
for k := range session.Values {
|
||||||
|
delete(session.Values, k)
|
||||||
|
}
|
||||||
|
if _, err := appDbExec("delete from sessions where id = @id", sql.Named("id", session.ID)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dbSessionStore) load(session *sessions.Session) (err error) {
|
||||||
|
row, err := appDbQueryRow("select data, created, modified, expires from sessions where id = @id", sql.Named("id", session.ID))
|
||||||
|
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 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
session.Values[sessionCreatedOn] = created
|
||||||
|
session.Values[sessionModifiedOn] = modified
|
||||||
|
session.Values[sessionExpiresOn] = expires
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dbSessionStore) insert(session *sessions.Session) (err error) {
|
||||||
|
created := time.Now()
|
||||||
|
modified := time.Now()
|
||||||
|
expires := time.Now().Add(time.Second * time.Duration(session.Options.MaxAge))
|
||||||
|
delete(session.Values, sessionCreatedOn)
|
||||||
|
delete(session.Values, sessionExpiresOn)
|
||||||
|
delete(session.Values, sessionModifiedOn)
|
||||||
|
encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, s.codecs...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res, err := appDbExec("insert into sessions(data, created, modified, expires) values(@data, @created, @modified, @expires)",
|
||||||
|
sql.Named("data", encoded), sql.Named("created", created.Local().String()), sql.Named("modified", modified.Local().String()), sql.Named("expires", expires.Local().String()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lastInserted, err := res.LastInsertId()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
session.ID = fmt.Sprintf("%d", lastInserted)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dbSessionStore) save(session *sessions.Session) (err error) {
|
||||||
|
if session.IsNew {
|
||||||
|
return s.insert(session)
|
||||||
|
}
|
||||||
|
delete(session.Values, sessionCreatedOn)
|
||||||
|
delete(session.Values, sessionExpiresOn)
|
||||||
|
delete(session.Values, sessionModifiedOn)
|
||||||
|
encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, s.codecs...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = appDbExec("update sessions set data = @data, modified = @modified where id = @id",
|
||||||
|
sql.Named("data", encoded), sql.Named("modified", time.Now().Local().String()), sql.Named("id", session.ID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
<input type="hidden" name="captchaheaders" value="{{ .Data.captchaheaders }}">
|
<input type="hidden" name="captchaheaders" value="{{ .Data.captchaheaders }}">
|
||||||
<input type="hidden" name="captchabody" value="{{ .Data.captchabody }}">
|
<input type="hidden" name="captchabody" value="{{ .Data.captchabody }}">
|
||||||
<input type="hidden" name="captchaid" value="{{ .Data.captchaid }}">
|
<input type="hidden" name="captchaid" value="{{ .Data.captchaid }}">
|
||||||
<input type="text" name="digits" placeholder="{{ string .Blog.Lang "captchainstructions" }}">
|
<input type="text" name="digits" placeholder="{{ string .Blog.Lang "captchainstructions" }}" required>
|
||||||
<input class="fw" type="submit" value="{{ string .Blog.Lang "submit" }}">
|
<input class="fw" type="submit" value="{{ string .Blog.Lang "submit" }}">
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -2,12 +2,14 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -77,15 +79,17 @@ func (m *mention) verifyMention() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req.Header.Set(userAgent, appUserAgent)
|
var resp *http.Response
|
||||||
if strings.HasPrefix(m.Source, appConfig.Server.PublicAddress) {
|
if strings.HasPrefix(m.Source, appConfig.Server.PublicAddress) {
|
||||||
// Set authentication
|
rec := httptest.NewRecorder()
|
||||||
c, _ := createTokenCookie()
|
d.ServeHTTP(rec, req.WithContext(context.WithValue(req.Context(), loggedInKey, true)))
|
||||||
req.AddCookie(c)
|
resp = rec.Result()
|
||||||
}
|
} else {
|
||||||
resp, err := appHttpClient.Do(req)
|
req.Header.Set(userAgent, appUserAgent)
|
||||||
if err != nil {
|
resp, err = appHttpClient.Do(req)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
err = m.verifyReader(resp.Body)
|
err = m.verifyReader(resp.Body)
|
||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
|
|
Loading…
Reference in New Issue