2021-01-23 16:24:47 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2021-07-24 11:35:26 +00:00
|
|
|
"context"
|
2021-01-23 16:24:47 +00:00
|
|
|
"encoding/base64"
|
|
|
|
"encoding/json"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
2021-07-24 11:35:26 +00:00
|
|
|
"strings"
|
2021-01-23 16:24:47 +00:00
|
|
|
|
|
|
|
"github.com/dchest/captcha"
|
2021-06-28 20:17:18 +00:00
|
|
|
"go.goblog.app/app/pkgs/contenttype"
|
2021-01-23 16:24:47 +00:00
|
|
|
)
|
|
|
|
|
2021-07-24 11:35:26 +00:00
|
|
|
const captchaSolvedKey contextKey = "captchaSolved"
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (a *goBlog) captchaMiddleware(next http.Handler) http.Handler {
|
2021-01-23 16:24:47 +00:00
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2021-07-24 11:35:26 +00:00
|
|
|
// Check if captcha already solved
|
|
|
|
if solved, ok := r.Context().Value(captchaSolvedKey).(bool); ok && solved {
|
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Check Cookie
|
2021-06-06 12:39:42 +00:00
|
|
|
ses, err := a.captchaSessions.Get(r, "c")
|
2021-05-14 16:24:02 +00:00
|
|
|
if err == nil && ses != nil {
|
|
|
|
if captcha, ok := ses.Values["captcha"]; ok && captcha.(bool) {
|
2021-07-24 11:35:26 +00:00
|
|
|
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), captchaSolvedKey, true)))
|
2021-01-23 16:24:47 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2021-07-24 11:35:26 +00:00
|
|
|
// 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
|
2021-01-23 16:24:47 +00:00
|
|
|
_ = r.Body.Close()
|
|
|
|
if len(b) == 0 {
|
|
|
|
// Maybe it's a form
|
|
|
|
_ = r.ParseForm()
|
|
|
|
b = []byte(r.PostForm.Encode())
|
|
|
|
}
|
2021-06-19 06:37:16 +00:00
|
|
|
a.renderWithStatusCode(w, r, http.StatusUnauthorized, templateCaptcha, &renderData{
|
2021-01-23 16:24:47 +00:00
|
|
|
Data: map[string]string{
|
|
|
|
"captchamethod": r.Method,
|
|
|
|
"captchaheaders": base64.StdEncoding.EncodeToString(h),
|
|
|
|
"captchabody": base64.StdEncoding.EncodeToString(b),
|
|
|
|
"captchaid": captcha.New(),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (a *goBlog) checkIsCaptcha(next http.Handler) http.Handler {
|
2021-01-23 16:24:47 +00:00
|
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
2021-06-06 12:39:42 +00:00
|
|
|
if !a.checkCaptcha(rw, r) {
|
2021-01-23 16:24:47 +00:00
|
|
|
next.ServeHTTP(rw, r)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (a *goBlog) checkCaptcha(w http.ResponseWriter, r *http.Request) bool {
|
2021-05-14 16:24:02 +00:00
|
|
|
if r.Method != http.MethodPost {
|
|
|
|
return false
|
|
|
|
}
|
2021-07-24 11:35:26 +00:00
|
|
|
if !strings.Contains(r.Header.Get(contentType), contenttype.WWWForm) {
|
2021-05-14 16:24:02 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
if r.FormValue("captchaaction") != "captcha" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
// Prepare 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 and create cookie
|
|
|
|
if captcha.VerifyString(r.FormValue("captchaid"), r.FormValue("digits")) {
|
2021-06-06 12:39:42 +00:00
|
|
|
ses, err := a.captchaSessions.Get(r, "c")
|
2021-05-14 16:24:02 +00:00
|
|
|
if err != nil {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
2021-05-14 16:24:02 +00:00
|
|
|
return true
|
2021-01-23 16:24:47 +00:00
|
|
|
}
|
2021-05-14 16:24:02 +00:00
|
|
|
ses.Values["captcha"] = true
|
2021-07-24 11:35:26 +00:00
|
|
|
err = a.captchaSessions.Save(r, w, ses)
|
2021-05-14 16:24:02 +00:00
|
|
|
if err != nil {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
2021-05-14 16:24:02 +00:00
|
|
|
return true
|
2021-01-23 16:24:47 +00:00
|
|
|
}
|
2021-07-24 11:35:26 +00:00
|
|
|
req = req.WithContext(context.WithValue(req.Context(), captchaSolvedKey, true))
|
2021-01-23 16:24:47 +00:00
|
|
|
}
|
2021-05-14 16:24:02 +00:00
|
|
|
// Serve original request
|
2021-06-06 12:39:42 +00:00
|
|
|
a.d.ServeHTTP(w, req)
|
2021-05-14 16:24:02 +00:00
|
|
|
return true
|
2021-01-23 16:24:47 +00:00
|
|
|
}
|