diff options
Diffstat (limited to 'services/context/captcha.go')
-rw-r--r-- | services/context/captcha.go | 118 |
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) + } +} |