0404662e99
- This is a fork of https://github.com/dchest/captcha, as
https://gitea.com/go-chi/captcha is a fork of
github.com/go-macaron/captcha which is a fork (although not properly
credited) of a older version of https://github.com/dchest/captcha. Hence
why I've just forked the original.
- The fork includes some QoL improvements (uses standard library for
determistic RNG instead of rolling your own crypto), and removal of
audio support (500KiB unused data that bloated the binary otherwise).
Flips the image over the x-asis.
47270f2b55
..main
- This move is needed for the next commit, because
gitea.com/go-chi/captcha included the gitea.com/go-chi/cache dependency.
118 lines
3.4 KiB
Go
118 lines
3.4 KiB
Go
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package context
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"code.gitea.io/gitea/modules/base"
|
|
"code.gitea.io/gitea/modules/cache"
|
|
"code.gitea.io/gitea/modules/hcaptcha"
|
|
"code.gitea.io/gitea/modules/log"
|
|
"code.gitea.io/gitea/modules/mcaptcha"
|
|
"code.gitea.io/gitea/modules/recaptcha"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
"code.gitea.io/gitea/modules/turnstile"
|
|
mc "gitea.com/go-chi/cache"
|
|
|
|
"code.forgejo.org/go-chi/captcha"
|
|
)
|
|
|
|
var (
|
|
imageCaptchaOnce sync.Once
|
|
imageCachePrefix = "captcha:"
|
|
)
|
|
|
|
type imageCaptchaStore struct {
|
|
c mc.Cache
|
|
}
|
|
|
|
func (c *imageCaptchaStore) Set(id string, digits []byte) {
|
|
if err := c.c.Put(imageCachePrefix+id, string(digits), int64(captcha.Expiration.Seconds())); err != nil {
|
|
log.Error("Couldn't store captcha cache for %q: %v", id, err)
|
|
}
|
|
}
|
|
|
|
func (c *imageCaptchaStore) Get(id string, clear bool) (digits []byte) {
|
|
val, ok := c.c.Get(imageCachePrefix + id).(string)
|
|
if !ok {
|
|
return digits
|
|
}
|
|
|
|
if clear {
|
|
if err := c.c.Delete(imageCachePrefix + id); err != nil {
|
|
log.Error("Couldn't delete captcha cache for %q: %v", id, err)
|
|
}
|
|
}
|
|
|
|
return []byte(val)
|
|
}
|
|
|
|
// GetImageCaptcha returns image captcha ID.
|
|
func GetImageCaptcha() string {
|
|
imageCaptchaOnce.Do(func() {
|
|
captcha.SetCustomStore(&imageCaptchaStore{c: cache.GetCache()})
|
|
})
|
|
return captcha.New()
|
|
}
|
|
|
|
// SetCaptchaData sets common captcha data
|
|
func SetCaptchaData(ctx *Context) {
|
|
if !setting.Service.EnableCaptcha {
|
|
return
|
|
}
|
|
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
|
|
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
|
|
ctx.Data["Captcha"] = GetImageCaptcha()
|
|
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
|
|
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
|
|
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
|
|
ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
|
|
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
|
|
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
|
|
}
|
|
|
|
const (
|
|
imgCaptchaIDField = "img-captcha-id"
|
|
imgCaptchaResponseField = "img-captcha-response"
|
|
gRecaptchaResponseField = "g-recaptcha-response"
|
|
hCaptchaResponseField = "h-captcha-response"
|
|
mCaptchaResponseField = "m-captcha-response"
|
|
cfTurnstileResponseField = "cf-turnstile-response"
|
|
)
|
|
|
|
// VerifyCaptcha verifies Captcha data
|
|
// No-op if captchas are not enabled
|
|
func VerifyCaptcha(ctx *Context, tpl base.TplName, form any) {
|
|
if !setting.Service.EnableCaptcha {
|
|
return
|
|
}
|
|
|
|
var valid bool
|
|
var err error
|
|
switch setting.Service.CaptchaType {
|
|
case setting.ImageCaptcha:
|
|
valid = captcha.VerifyString(ctx.Req.Form.Get(imgCaptchaIDField), ctx.Req.Form.Get(imgCaptchaResponseField))
|
|
case setting.ReCaptcha:
|
|
valid, err = recaptcha.Verify(ctx, ctx.Req.Form.Get(gRecaptchaResponseField))
|
|
case setting.HCaptcha:
|
|
valid, err = hcaptcha.Verify(ctx, ctx.Req.Form.Get(hCaptchaResponseField))
|
|
case setting.MCaptcha:
|
|
valid, err = mcaptcha.Verify(ctx, ctx.Req.Form.Get(mCaptchaResponseField))
|
|
case setting.CfTurnstile:
|
|
valid, err = turnstile.Verify(ctx, ctx.Req.Form.Get(cfTurnstileResponseField))
|
|
default:
|
|
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("unknown Captcha Type: %s", setting.Service.CaptchaType))
|
|
return
|
|
}
|
|
if err != nil {
|
|
log.Debug("Captcha Verify failed: %v", err)
|
|
}
|
|
|
|
if !valid {
|
|
ctx.Data["Err_Captcha"] = true
|
|
ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tpl, form)
|
|
}
|
|
}
|