summaryrefslogtreecommitdiffstats
path: root/services/context/captcha.go
diff options
context:
space:
mode:
Diffstat (limited to 'services/context/captcha.go')
-rw-r--r--services/context/captcha.go118
1 files changed, 118 insertions, 0 deletions
diff --git a/services/context/captcha.go b/services/context/captcha.go
new file mode 100644
index 0000000..da837ac
--- /dev/null
+++ b/services/context/captcha.go
@@ -0,0 +1,118 @@
+// 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 "code.forgejo.org/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)
+ }
+}