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 Recaptcha functionality to Gitea #4044

Merged
merged 30 commits into from Jul 5, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a467186
Add Recaptcha functionality to Gitea
flynnnnnnnnnn May 25, 2018
55bbfac
Add comment to struct
May 25, 2018
76b57f1
Add comment to function
May 25, 2018
0b12d2a
Fix import order
May 25, 2018
f6d9d2b
Remove helper functions
May 25, 2018
a39289d
Remove empty line and initialize Response differently
May 25, 2018
22d6265
Move script to footer
May 25, 2018
02ac7e3
Remove script from body
May 25, 2018
f203c2f
Remove script from body
May 25, 2018
7e6a60a
Add styles to Less
May 25, 2018
e787c7f
Remove inline styles
May 25, 2018
1effea1
Remove inline styles
May 25, 2018
b49c864
add field to form
flynnnnnnnnnn May 25, 2018
313e7dd
Switch to form field
May 25, 2018
d9e4392
Switch to form field
May 25, 2018
5039fe1
add built stylesheet
flynnnnnnnnnn May 25, 2018
f4ddfa1
Update built stylesheet
May 25, 2018
f2199d1
Responsive recaptcha styles
May 25, 2018
59cba2c
Merge branch 'master' into recaptcha
Jun 7, 2018
5aa9afb
Merge branch 'master' into recaptcha
Jul 4, 2018
628ca1b
change to captcha type to change between captchas
flynnnnnnnnnn Jul 4, 2018
8357eb5
Delete signup_inner.tmpl.save
Jul 4, 2018
7ff8e14
Delete signup_openid_register.tmpl.save
Jul 4, 2018
3239fad
update changes to css
flynnnnnnnnnn Jul 4, 2018
795019c
Merge branch 'master' into recaptcha
Jul 4, 2018
6786290
type issues
Jul 4, 2018
d32a204
conflicting types
Jul 4, 2018
93b4909
use const
flynnnnnnnnnn Jul 5, 2018
a2b9ba2
Merge branch 'master' into recaptcha
Jul 5, 2018
1812cd0
Merge branch 'master' into recaptcha
lunny Jul 5, 2018
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
7 changes: 6 additions & 1 deletion custom/conf/app.ini.sample
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,12 @@ ENABLE_NOTIFY_MAIL = false
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
; Enable captcha validation for registration
ENABLE_CAPTCHA = true
ENABLE_CAPTCHA = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not change default as this way it will be breaking change

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value is actually false but the documentation is wrong

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi. Yes false is the default, and I changed it to be correct like @JonasFranzDEV said.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like you use a captcha type and currently you have three options: disabled, image and recaptcha and default is disabled.

; Enable recaptcha to use Google's recaptcha service
; Go to https://www.google.com/recaptcha/admin to sign up for a key
ENABLE_RECAPTCHA = false
RECAPTCHA_SECRET =
RECAPTCHA_SITEKEY =
; Default value for KeepEmailPrivate
; Each new user will get the value of this setting copied into their profile
DEFAULT_KEEP_EMAIL_PRIVATE = false
Expand Down
5 changes: 4 additions & 1 deletion docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,10 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `ENABLE_REVERSE_PROXY_AUTHENTICATION`: **false**: Enable this to allow reverse proxy authentication.
- `ENABLE_REVERSE_PROXY_AUTO_REGISTRATION`: **false**: Enable this to allow auto-registration
for reverse authentication.
- `ENABLE_CAPTCHA`: **true**: Enable this to use captcha validation for registration.
- `ENABLE_CAPTCHA`: **false**: Enable this to use captcha validation for registration.
- `ENABLE_RECAPTCHA`: **false**: Enable this to use Google's Recaptcha service for registration
- `RECAPTCHA_SECRET`: **""**: Go to https://www.google.com/recaptcha/admin to get a secret for recaptcha
- `RECAPTCHA_SITEKEY`: **""**: Go to https://www.google.com/recaptcha/admin to get a sitekey for recaptcha

## Webhook (`webhook`)

Expand Down
48 changes: 48 additions & 0 deletions modules/recaptcha/recaptcha.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package recaptcha

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"time"

"code.gitea.io/gitea/modules/setting"
)

// Response is the structure of JSON returned from API
type Response struct {
Success bool `json:"success"`
ChallengeTS time.Time `json:"challenge_ts"`
Hostname string `json:"hostname"`
ErrorCodes []string `json:"error-codes"`
}

const apiURL = "https://www.google.com/recaptcha/api/siteverify"

// Verify calls Google Recaptcha API to verify token
func Verify(response string) (bool, error) {
resp, err := http.PostForm(apiURL,
url.Values{"secret": {setting.Service.RecaptchaSecret}, "response": {response}})
if err != nil {
return false, fmt.Errorf("Failed to send CAPTCHA response: %s", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return false, fmt.Errorf("Failed to read CAPTCHA response: %s", err)
}
jsonResponse := Response{Success: false} // set a default of fail for CAPTCHA
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value of Success is false => No need for initializing it.

err = json.Unmarshal(body, &jsonResponse)
if err != nil {
return false, fmt.Errorf("Failed to parse CAPTCHA response: %s", err)
}

return jsonResponse.Success, nil

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove empty line

}
8 changes: 7 additions & 1 deletion modules/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,9 @@ var Service struct {
EnableReverseProxyAuth bool
EnableReverseProxyAutoRegister bool
EnableCaptcha bool
EnableRecaptcha bool
RecaptchaSecret string
RecaptchaSitekey string
DefaultKeepEmailPrivate bool
DefaultAllowCreateOrganization bool
EnableTimetracking bool
Expand All @@ -1182,7 +1185,10 @@ func newService() {
Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool()
Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool(false)
Service.EnableRecaptcha = sec.Key("ENABLE_RECAPTCHA").MustBool(false)
Service.RecaptchaSecret = sec.Key("RECAPTCHA_SECRET").MustString("")
Service.RecaptchaSitekey = sec.Key("RECAPTCHA_SITEKEY").MustString("")
Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool()
Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true)
Service.EnableTimetracking = sec.Key("ENABLE_TIMETRACKING").MustBool(true)
Expand Down
6 changes: 6 additions & 0 deletions modules/templates/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ func NewFuncMap() []template.FuncMap {
"ParseDeadline": func(deadline string) []string {
return strings.Split(deadline, "|")
},
"EnableRecaptcha": func() bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need a helper function for that? Please use ctx.Data.

return setting.Service.EnableRecaptcha
},
"RecaptchaSitekey": func() string {
return setting.Service.RecaptchaSitekey
},
}}
}

Expand Down
33 changes: 33 additions & 0 deletions routers/user/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/recaptcha"
"code.gitea.io/gitea/modules/setting"

"github.com/go-macaron/captcha"
Expand Down Expand Up @@ -640,6 +641,8 @@ func LinkAccount(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("link_account")
ctx.Data["LinkAccountMode"] = true
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
ctx.Data["EnableRecaptcha"] = setting.Service.EnableRecaptcha
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false

Expand All @@ -665,6 +668,8 @@ func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) {
ctx.Data["LinkAccountMode"] = true
ctx.Data["LinkAccountModeSignIn"] = true
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
ctx.Data["EnableRecaptcha"] = setting.Service.EnableRecaptcha
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false

Expand Down Expand Up @@ -731,6 +736,8 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
ctx.Data["LinkAccountMode"] = true
ctx.Data["LinkAccountModeRegister"] = true
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
ctx.Data["EnableRecaptcha"] = setting.Service.EnableRecaptcha
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false

Expand Down Expand Up @@ -760,6 +767,16 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
return
}

if setting.Service.EnableRecaptcha {
ctx.Req.ParseForm()
valid, _ := recaptcha.Verify(ctx.Req.Form.Get("g-recaptcha-response"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add g-recaptcha-response as form field to form.

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

if (len(strings.TrimSpace(form.Password)) > 0 || len(strings.TrimSpace(form.Retype)) > 0) && form.Password != form.Retype {
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplLinkAccount, &form)
Expand Down Expand Up @@ -857,6 +874,9 @@ func SignUp(ctx *context.Context) {

ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha

ctx.Data["EnableRecaptcha"] = setting.Service.EnableRecaptcha
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey

ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration

ctx.HTML(200, tplSignUp)
Expand All @@ -870,6 +890,9 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo

ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha

ctx.Data["EnableRecaptcha"] = setting.Service.EnableRecaptcha
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey

//Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
if !setting.Service.ShowRegistrationButton {
ctx.Error(403)
Expand All @@ -887,6 +910,16 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
return
}

if setting.Service.EnableRecaptcha {
ctx.Req.ParseForm()
valid, _ := recaptcha.Verify(ctx.Req.Form.Get("g-recaptcha-response"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add g-recaptcha-response as form field to auth.RegisterForm.

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

if form.Password != form.Retype {
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplSignUp, &form)
Expand Down
15 changes: 15 additions & 0 deletions routers/user/auth_openid.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/recaptcha"
"code.gitea.io/gitea/modules/setting"

"github.com/go-macaron/captcha"
Expand Down Expand Up @@ -308,6 +309,8 @@ func RegisterOpenID(ctx *context.Context) {
ctx.Data["PageIsOpenIDRegister"] = true
ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
ctx.Data["EnableRecaptcha"] = setting.Service.EnableRecaptcha
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["OpenID"] = oid
userName, _ := ctx.Session.Get("openid_determined_username").(string)
if userName != "" {
Expand All @@ -333,6 +336,8 @@ func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.Si
ctx.Data["PageIsOpenIDRegister"] = true
ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
ctx.Data["EnableRecaptcha"] = setting.Service.EnableRecaptcha
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["OpenID"] = oid

if setting.Service.EnableCaptcha && !cpt.VerifyReq(ctx.Req) {
Expand All @@ -341,6 +346,16 @@ func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.Si
return
}

if setting.Service.EnableRecaptcha {
ctx.Req.ParseForm()
valid, _ := recaptcha.Verify(ctx.Req.Form.Get("g-recaptcha-response"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

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

len := setting.MinPasswordLength
if len < 256 {
len = 256
Expand Down
7 changes: 7 additions & 0 deletions templates/user/auth/signup_inner.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@
</div>
{{end}}

{{if .EnableRecaptcha}}
<div class="inline field required">
<div class="g-recaptcha" data-sitekey="{{ .RecaptchaSitekey }}" style="margin: 0 auto; width: 304px; padding-left: 30px"></div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use inline css. Please add the style to the .less files.

<script src="https://www.google.com/recaptcha/api.js" async></script>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scripts should be placed in footer.

</div>
{{end}}

<div class="inline field">
<label></label>
<button class="ui green button">{{.i18n.Tr "auth.create_new_account"}}</button>
Expand Down
6 changes: 6 additions & 0 deletions templates/user/auth/signup_openid_register.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
<input id="captcha" name="captcha" value="{{.captcha}}" autocomplete="off">
</div>
{{end}}
{{if .EnableRecaptcha}}
<div class="inline field required">
<div class="g-recaptcha" data-sitekey="{{ .RecaptchaSitekey }}" style="margin: 0 auto; width: 304px; padding-left: 30px"></div>
<script src="https://www.google.com/recaptcha/api.js" async></script>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

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