From a0f1cd581404c50b440800cdf73f14c290b7e128 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Thu, 7 Sep 2023 22:38:23 +0100 Subject: [PATCH] captcha: fix missing images The captcha library's data struct wasn't being serialized/deserialized fully, meaning the image was never stored. I never really wanted it to be stored anyway, but as a compromise, the invite daemon now deletes captcha images from the DB 20 minutes after generation. --- api-invites.go | 17 +++++++++++++++++ storage.go | 9 +++++++-- views.go | 40 ++++++++++++++++++++++++---------------- 3 files changed, 48 insertions(+), 18 deletions(-) diff --git a/api-invites.go b/api-invites.go index 8042e2e..9a58336 100644 --- a/api-invites.go +++ b/api-invites.go @@ -13,9 +13,26 @@ import ( "github.com/timshannon/badgerhold/v4" ) +const ( + CAPTCHA_VALIDITY = 20 * 60 // Seconds +) + func (app *appContext) checkInvites() { currentTime := time.Now() for _, data := range app.storage.GetInvites() { + captchas := data.Captchas + captchasExpired := false + for key, capt := range data.Captchas { + if time.Now().After(capt.Generated.Add(CAPTCHA_VALIDITY * time.Second)) { + delete(captchas, key) + captchasExpired = true + } + } + if captchasExpired { + data.Captchas = captchas + app.storage.SetInvitesKey(data.Code, data) + } + if data.IsReferral { continue } diff --git a/storage.go b/storage.go index 5d8a527..4591c1b 100644 --- a/storage.go +++ b/storage.go @@ -11,7 +11,6 @@ import ( "time" "github.com/hrfee/mediabrowser" - "github.com/steambap/captcha" "github.com/timshannon/badgerhold/v4" ) @@ -501,12 +500,18 @@ type Invite struct { Notify map[string]map[string]bool `json:"notify"` Profile string `json:"profile"` Label string `json:"label,omitempty"` - Captchas map[string]*captcha.Data // Map of Captcha IDs to answers + Captchas map[string]Captcha // Map of Captcha IDs to images & answers IsReferral bool `json:"is_referral" badgerhold:"index"` ReferrerJellyfinID string `json:"referrer_id"` ReferrerTemplateForProfile string } +type Captcha struct { + Answer string + Image []byte // image/png + Generated time.Time +} + type Lang struct { AdminPath string chosenAdminLang string diff --git a/views.go b/views.go index 704a35d..f1f7fdd 100644 --- a/views.go +++ b/views.go @@ -1,6 +1,8 @@ package main import ( + "bufio" + "bytes" "encoding/json" "html/template" "io" @@ -372,20 +374,16 @@ func (app *appContext) GetCaptcha(gc *gin.Context) { "contactMessage": app.config.Section("ui").Key("contact_message").String(), }) } - var capt *captcha.Data + var capt Captcha + ok = true if inv.Captchas != nil { - capt = inv.Captchas[captchaID] + capt, ok = inv.Captchas[captchaID] } - if capt == nil { + if !ok { respondBool(400, false, gc) return } - if err := capt.WriteImage(gc.Writer); err != nil { - app.err.Printf("Failed to write CAPTCHA image: %v", err) - respondBool(500, false, gc) - return - } - gc.Status(200) + gc.Data(200, "image/png", capt.Image) return } @@ -414,10 +412,20 @@ func (app *appContext) GenCaptcha(gc *gin.Context) { return } if inv.Captchas == nil { - inv.Captchas = map[string]*captcha.Data{} + inv.Captchas = map[string]Captcha{} } captchaID := genAuthToken() - inv.Captchas[captchaID] = capt + var buf bytes.Buffer + if err := capt.WriteImage(bufio.NewWriter(&buf)); err != nil { + app.err.Printf("Failed to render captcha: %v", err) + respondBool(500, false, gc) + return + } + inv.Captchas[captchaID] = Captcha{ + Answer: capt.Text, + Image: buf.Bytes(), + Generated: time.Now(), + } app.storage.SetInvitesKey(code, inv) gc.JSON(200, genCaptchaDTO{captchaID}) return @@ -437,7 +445,7 @@ func (app *appContext) verifyCaptcha(code, id, text string) bool { app.debug.Printf("Couldn't find Captcha \"%s\"", id) return false } - return strings.ToLower(c.Text) == strings.ToLower(text) + return strings.ToLower(c.Answer) == strings.ToLower(text) } // reCAPTCHA @@ -504,15 +512,15 @@ func (app *appContext) VerifyCaptcha(gc *gin.Context) { }) return } - var capt *captcha.Data + var capt Captcha if inv.Captchas != nil { - capt = inv.Captchas[captchaID] + capt, ok = inv.Captchas[captchaID] } - if capt == nil { + if !ok { respondBool(400, false, gc) return } - if strings.ToLower(capt.Text) != strings.ToLower(text) { + if strings.ToLower(capt.Answer) != strings.ToLower(text) { respondBool(400, false, gc) return }