Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to enable CAPTCHA validation for login #21638

Merged
merged 28 commits into from Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ad92a8e
Allow to enable CAPTCHA validation for login
Oct 31, 2022
f3cef9c
fix some stupid things
Oct 31, 2022
11dc7ef
as per KN4CK3R
zeripath Oct 31, 2022
c6da162
Merge remote-tracking branch 'origin/main' into REQUIRE_CAPTCHA_FOR_L…
zeripath Oct 31, 2022
3d70bc9
Consolidate CAPTCHA set-up and verification code
zeripath Oct 31, 2022
6ad5f14
fix type
Nov 1, 2022
9c2351f
Merge pull request #1 from zeripath/patch-21638-captcha-for-login
Nov 1, 2022
d02b04f
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
Nov 8, 2022
b839e86
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 10, 2022
86a2bd3
CAPTCHA response field should be private variables
Nov 10, 2022
674847d
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
Nov 11, 2022
12f07ec
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 11, 2022
66c458f
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 11, 2022
7569531
Update modules/context/captcha.go
Nov 12, 2022
34125ae
Update modules/context/captcha.go
Nov 12, 2022
1774038
Add comment to CAPTCHA filed <label></label>
Nov 12, 2022
7c676d0
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
Nov 12, 2022
cc7d1c4
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 15, 2022
b7700ba
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 15, 2022
ced03dd
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 19, 2022
ebe6456
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 19, 2022
7e19272
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 19, 2022
9a09578
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 21, 2022
9d93336
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 21, 2022
c0b1133
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 21, 2022
7033873
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 22, 2022
c9d235d
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 22, 2022
1eb6e2e
Merge branch 'main' into REQUIRE_CAPTCHA_FOR_LOGIN
lunny Nov 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,9 @@ ROUTER = console
;; Enable captcha validation for registration
;ENABLE_CAPTCHA = false
;;
;; Enable this to require captcha validation for login
;REQUIRE_CAPTCHA_FOR_LOGIN = false
;;
;; Type of captcha you want to use. Options: image, recaptcha, hcaptcha, mcaptcha.
;CAPTCHA_TYPE = image
;;
Expand Down
1 change: 1 addition & 0 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ Certain queues have defaults that override the defaults set in `[queue]` (this o
- `ENABLE_REVERSE_PROXY_FULL_NAME`: **false**: Enable this to allow to auto-registration with a
provided full name for the user.
- `ENABLE_CAPTCHA`: **false**: Enable this to use captcha validation for registration.
- `REQUIRE_CAPTCHA_FOR_LOGIN`: **false**: Enable this to require captcha validation for login. You also must enable `ENABLE_CAPTCHA`.
- `REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA`: **false**: Enable this to force captcha validation
even for External Accounts (i.e. GitHub, OpenID Connect, etc). You also must enable `ENABLE_CAPTCHA`.
- `CAPTCHA_TYPE`: **image**: \[image, recaptcha, hcaptcha, mcaptcha\]
Expand Down
3 changes: 2 additions & 1 deletion docs/content/doc/advanced/config-cheat-sheet.zh-cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ menu:
- `ENABLE_NOTIFY_MAIL`: 是否发送工单创建等提醒邮件,需要 `Mailer` 被激活。
- `ENABLE_REVERSE_PROXY_AUTHENTICATION`: 允许反向代理认证,更多细节见:https://github.com/gogits/gogs/issues/165
- `ENABLE_REVERSE_PROXY_AUTO_REGISTRATION`: 允许通过反向认证做自动注册。
- `ENABLE_CAPTCHA`: 注册时使用图片验证码。
- `ENABLE_CAPTCHA`: **false**: 注册时使用图片验证码。
- `REQUIRE_CAPTCHA_FOR_LOGIN`: **false**: 登录时需要图片验证码。需要同时开启 `ENABLE_CAPTCHA`。

### Service - Expore (`service.explore`)

Expand Down
2 changes: 2 additions & 0 deletions modules/setting/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ var Service = struct {
EnableReverseProxyEmail bool
EnableReverseProxyFullName bool
EnableCaptcha bool
RequireCaptchaForLogin bool
RequireExternalRegistrationCaptcha bool
RequireExternalRegistrationPassword bool
CaptchaType string
Expand Down Expand Up @@ -130,6 +131,7 @@ func newService() {
Service.EnableReverseProxyEmail = sec.Key("ENABLE_REVERSE_PROXY_EMAIL").MustBool()
Service.EnableReverseProxyFullName = sec.Key("ENABLE_REVERSE_PROXY_FULL_NAME").MustBool()
Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool(false)
Service.RequireCaptchaForLogin = sec.Key("REQUIRE_CAPTCHA_FOR_LOGIN").MustBool(false)
Service.RequireExternalRegistrationCaptcha = sec.Key("REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA").MustBool(Service.EnableCaptcha)
Service.RequireExternalRegistrationPassword = sec.Key("REQUIRE_EXTERNAL_REGISTRATION_PASSWORD").MustBool()
Service.CaptchaType = sec.Key("CAPTCHA_TYPE").MustString(ImageCaptcha)
Expand Down
48 changes: 48 additions & 0 deletions routers/web/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,17 @@ func SignIn(ctx *context.Context) {
ctx.Data["PageIsLogin"] = true
ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled()

if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin {
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
ctx.Data["Captcha"] = context.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.HTML(http.StatusOK, tplSignIn)
}

Expand All @@ -196,6 +207,43 @@ func SignInPost(ctx *context.Context) {
}

form := web.GetForm(ctx).(*forms.SignInForm)

if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin {
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
ctx.Data["Captcha"] = context.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

var valid bool
var err error
switch setting.Service.CaptchaType {
case setting.ImageCaptcha:
valid = context.GetImageCaptcha().VerifyReq(ctx.Req)
case setting.ReCaptcha:
valid, err = recaptcha.Verify(ctx, form.GRecaptchaResponse)
case setting.HCaptcha:
valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse)
case setting.MCaptcha:
valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse)
default:
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
return
}
if err != nil {
log.Debug("%s", err.Error())
}

if !valid {
ctx.Data["Err_Captcha"] = true
ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplSignIn, &form)
return
}
}

u, source, err := auth_service.UserSignIn(form.UserName, form.Password)
if err != nil {
if user_model.IsErrUserNotExist(err) || user_model.IsErrEmailAddressNotExist(err) {
Expand Down
4 changes: 4 additions & 0 deletions services/forms/user_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ type SignInForm struct {
// TODO remove required from password for SecondFactorAuthentication
Password string `binding:"Required;MaxSize(255)"`
Remember bool
// Captcha
GRecaptchaResponse string `form:"g-recaptcha-response"`
HcaptchaResponse string `form:"h-captcha-response"`
McaptchaResponse string `form:"m-captcha-response"`
}

// Validate validates the fields
Expand Down
27 changes: 27 additions & 0 deletions templates/user/auth/captcha.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{{if and .EnableCaptcha (eq .CaptchaType "image")}}
zeripath marked this conversation as resolved.
Show resolved Hide resolved
<div class="inline field">
<label></label>
This conversation was marked as resolved.
Show resolved Hide resolved
{{.Captcha.CreateHTML}}
</div>
<div class="required inline field {{if .Err_Captcha}}error{{end}}">
<label for="captcha">{{.locale.Tr "captcha"}}</label>
<input id="captcha" name="captcha" value="{{.captcha}}" autocomplete="off">
</div>
{{end}}
{{if and .EnableCaptcha (eq .CaptchaType "recaptcha")}}
<div class="inline field required">
<div class="g-recaptcha" data-sitekey="{{.RecaptchaSitekey}}"></div>
</div>
{{end}}
{{if and .EnableCaptcha (eq .CaptchaType "hcaptcha")}}
<div class="inline field required">
<div class="h-captcha" data-sitekey="{{.HcaptchaSitekey}}"></div>
</div>
{{end}}
{{if and .EnableCaptcha (eq .CaptchaType "mcaptcha")}}
<div class="inline field df ac db-small captcha-field">
<span>{{.locale.Tr "captcha"}}</span>
<div class="border-secondary w-100-small" id="mcaptcha__widget-container" style="width: 50%; height: 5em"></div>
<div class="m-captcha" data-sitekey="{{.McaptchaSitekey}}" data-instance-url="{{.McaptchaURL}}"></div>
</div>
{{end}}
2 changes: 2 additions & 0 deletions templates/user/auth/signin_inner.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
</div>
{{end}}

{{template "user/auth/captcha" .}}

<div class="inline field">
<label></label>
<button class="ui green button">
Expand Down
28 changes: 1 addition & 27 deletions templates/user/auth/signup_inner.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -34,34 +34,8 @@
<input id="retype" name="retype" type="password" value="{{.retype}}" autocomplete="new-password" required>
</div>
{{end}}
{{if and .EnableCaptcha (eq .CaptchaType "image")}}
<div class="inline field">
<label></label>
{{.Captcha.CreateHTML}}
</div>
<div class="required inline field {{if .Err_Captcha}}error{{end}}">
<label for="captcha">{{.locale.Tr "captcha"}}</label>
<input id="captcha" name="captcha" value="{{.captcha}}" autocomplete="off">
</div>
{{end}}
{{if and .EnableCaptcha (eq .CaptchaType "recaptcha")}}
<div class="inline field required">
<div class="g-recaptcha" data-sitekey="{{.RecaptchaSitekey}}"></div>
</div>
{{end}}
{{if and .EnableCaptcha (eq .CaptchaType "hcaptcha")}}
<div class="inline field required">
<div class="h-captcha" data-sitekey="{{.HcaptchaSitekey}}"></div>
</div>
{{end}}
{{if and .EnableCaptcha (eq .CaptchaType "mcaptcha")}}
<div class="inline field df ac db-small captcha-field">
<span>{{.locale.Tr "captcha"}}</span>
<div class="border-secondary w-100-small" id="mcaptcha__widget-container" style="width: 50%; height: 5em"></div>
<div class="m-captcha" data-sitekey="{{.McaptchaSitekey}}" data-instance-url="{{.McaptchaURL}}"></div>
</div>
{{end}}

{{template "user/auth/captcha" .}}

<div class="inline field">
<label></label>
Expand Down
28 changes: 3 additions & 25 deletions templates/user/auth/signup_openid_register.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,9 @@
<label for="email">{{.locale.Tr "email"}}</label>
<input id="email" name="email" type="email" value="{{.email}}" required>
</div>
{{if and .EnableCaptcha (eq .CaptchaType "image")}}
<div class="inline field">
<label></label>
{{.Captcha.CreateHTML}}
</div>
<div class="required inline field {{if .Err_Captcha}}error{{end}}">
<label for="captcha">{{.locale.Tr "captcha"}}</label>
<input id="captcha" name="captcha" value="{{.captcha}}" autocomplete="off">
</div>
{{end}}
{{if and .EnableCaptcha (eq .CaptchaType "recaptcha")}}
<div class="inline field required">
<div class="g-recaptcha" data-sitekey="{{.RecaptchaSitekey}}"></div>
</div>
{{end}}
{{if and .EnableCaptcha (eq .CaptchaType "hcaptcha")}}
<div class="inline field required">
<div class="h-captcha" data-sitekey="{{.HcaptchaSitekey}}"></div>
</div>
{{end}}
{{if and .EnableCaptcha (eq .CaptchaType "mcaptcha")}}
<div class="inline field required">
<div class="m-captcha" data-sitekey="{{.McaptchaSitekey}}" data-instance-url="{{.McaptchaURL}}"></div>
</div>
{{end}}

{{template "user/auth/captcha" .}}

<div class="inline field">
<label for="openid">OpenID URI</label>
<input id="openid" value="{{.OpenID}}" readonly>
Expand Down