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

UX Optional Password & Captcha for External Registration #5029

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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: 2 additions & 1 deletion modules/auth/user_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) bindin
type RegisterForm struct {
UserName string `binding:"Required;AlphaDashDot;MaxSize(40)"`
Email string `binding:"Required;Email;MaxSize(254)"`
Password string `binding:"Required;MaxSize(255)"`
Password string `binding:"MaxSize(255)"`
Retype string
GRecaptchaResponse string `form:"g-recaptcha-response"`
}
Expand Down Expand Up @@ -129,6 +129,7 @@ func (f *MustChangePasswordForm) Validate(ctx *macaron.Context, errs binding.Err
// SignInForm form for signing in with user/password
type SignInForm struct {
UserName string `binding:"Required;MaxSize(254)"`
// TODO remove required from password for SecondFactorAuthentication
Password string `binding:"Required;MaxSize(255)"`
Remember bool
}
Expand Down
4 changes: 4 additions & 0 deletions modules/setting/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ var Service struct {
EnableReverseProxyAutoRegister bool
EnableReverseProxyEmail bool
EnableCaptcha bool
RequireExternalRegistrationCaptcha bool
RequireExternalRegistrationPassword bool
CaptchaType string
RecaptchaSecret string
RecaptchaSitekey string
Expand Down Expand Up @@ -61,6 +63,8 @@ func newService() {
Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
Service.EnableReverseProxyEmail = sec.Key("ENABLE_REVERSE_PROXY_EMAIL").MustBool()
Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool(false)
Service.RequireExternalRegistrationCaptcha = sec.Key("REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA").MustBool()
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Service.RequireExternalRegistrationCaptcha = sec.Key("REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA").MustBool()
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)
Service.RecaptchaSecret = sec.Key("RECAPTCHA_SECRET").MustString("")
Service.RecaptchaSitekey = sec.Key("RECAPTCHA_SITEKEY").MustString("")
Expand Down
55 changes: 41 additions & 14 deletions routers/user/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
package user

import (
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"net/http"
Expand Down Expand Up @@ -131,6 +133,7 @@ func SignIn(ctx *context.Context) {
ctx.Data["OAuth2Providers"] = oauth2Providers
ctx.Data["Title"] = ctx.Tr("sign_in")
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
ctx.Data["AllowPassword"] = true
ctx.Data["PageIsSignIn"] = true
ctx.Data["PageIsLogin"] = true

Expand All @@ -150,6 +153,7 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) {
ctx.Data["OAuth2Providers"] = oauth2Providers
ctx.Data["Title"] = ctx.Tr("sign_in")
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
ctx.Data["AllowPassword"] = true
ctx.Data["PageIsSignIn"] = true
ctx.Data["PageIsLogin"] = true

Expand Down Expand Up @@ -699,10 +703,11 @@ func oAuth2UserLoginCallback(loginSource *models.LoginSource, request *http.Requ
func LinkAccount(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("link_account")
ctx.Data["LinkAccountMode"] = true
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
ctx.Data["EnableCaptcha"] = setting.Service.RequireExternalRegistrationCaptcha
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["AllowPassword"] = setting.Service.RequireExternalRegistrationPassword && !setting.Service.AllowOnlyExternalRegistration
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false

Expand Down Expand Up @@ -749,10 +754,12 @@ func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) {
ctx.Data["Title"] = ctx.Tr("link_account")
ctx.Data["LinkAccountMode"] = true
ctx.Data["LinkAccountModeSignIn"] = true
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
ctx.Data["EnableCaptcha"] = setting.Service.RequireExternalRegistrationCaptcha
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
// Only allow a password if local login is enabled (TODO) except if SecondFactorAuthentication is enabled
ctx.Data["AllowPassword"] = !setting.Service.AllowOnlyExternalRegistration
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false

Expand Down Expand Up @@ -827,10 +834,12 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
ctx.Data["Title"] = ctx.Tr("link_account")
ctx.Data["LinkAccountMode"] = true
ctx.Data["LinkAccountModeRegister"] = true
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
ctx.Data["EnableCaptcha"] = setting.Service.RequireExternalRegistrationCaptcha
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
// TODO implement the same for local accounts, once email-based Second Factor Auth is available
ctx.Data["AllowPassword"] = setting.Service.RequireExternalRegistrationPassword && !setting.Service.AllowOnlyExternalRegistration
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false

Expand All @@ -854,13 +863,13 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
return
}

if setting.Service.EnableCaptcha && setting.Service.CaptchaType == setting.ImageCaptcha && !cpt.VerifyReq(ctx.Req) {
if setting.Service.RequireExternalRegistrationCaptcha && setting.Service.CaptchaType == setting.ImageCaptcha && !cpt.VerifyReq(ctx.Req) {
ctx.Data["Err_Captcha"] = true
ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplLinkAccount, &form)
return
}

if setting.Service.EnableCaptcha && setting.Service.CaptchaType == setting.ReCaptcha {
if setting.Service.RequireExternalRegistrationCaptcha && setting.Service.CaptchaType == setting.ReCaptcha {
valid, _ := recaptcha.Verify(form.GRecaptchaResponse)
if !valid {
ctx.Data["Err_Captcha"] = true
Expand All @@ -869,15 +878,29 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
}
}

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)
return
}
if len(strings.TrimSpace(form.Password)) > 0 && len(form.Password) < setting.MinPasswordLength {
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplLinkAccount, &form)
return
if setting.Service.AllowOnlyExternalRegistration || !setting.Service.RequireExternalRegistrationPassword {
// Generating a random password a stop-gap shim to get around the password requirement
// Eventually the database should be changed to indicate "Second Factor" enabled accounts
// that do not introduce the security vulnerabilities of a password
bytes := make([]byte, 16)
_, err := rand.Read(bytes)
if nil != err {
ctx.ServerError("CreateUser", err)
return
}
form.Password = hex.EncodeToString(bytes)
} else {
// TODO Retype password should move to frontend JavaScript, not be required by server
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)
return
}
if len(strings.TrimSpace(form.Password)) > 0 && len(form.Password) < setting.MinPasswordLength {
ctx.Data["Err_Password"] = true
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplLinkAccount, &form)
return
}
}

loginSource, err := models.GetActiveOAuth2LoginSourceByName(gothUser.(goth.User).Provider)
Expand Down Expand Up @@ -973,6 +996,8 @@ func SignUp(ctx *context.Context) {
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey

ctx.Data["AllowPassword"] = true

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

ctx.HTML(200, tplSignUp)
Expand All @@ -989,6 +1014,8 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey

ctx.Data["AllowPassword"] = true

//Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
if !setting.Service.ShowRegistrationButton {
ctx.Error(403)
Expand Down
2 changes: 2 additions & 0 deletions templates/user/auth/signin_inner.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
<label for="user_name">{{.i18n.Tr "home.uname_holder"}}</label>
<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required>
</div>
{{if .AllowPassword}}
<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
<label for="password">{{.i18n.Tr "password"}}</label>
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
</div>
{{end}}
{{if not .LinkAccountMode}}
<div class="inline field">
<label></label>
Expand Down
18 changes: 10 additions & 8 deletions templates/user/auth/signup_inner.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@
<label for="email">{{.i18n.Tr "email"}}</label>
<input id="email" name="email" type="email" value="{{.email}}" required>
</div>
<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
<label for="password">{{.i18n.Tr "password"}}</label>
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
</div>
<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
<label for="retype">{{.i18n.Tr "re_type"}}</label>
<input id="retype" name="retype" type="password" value="{{.retype}}" autocomplete="off" required>
</div>
{{if .AllowPassword}}
<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
<label for="password">{{.i18n.Tr "password"}}</label>
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
</div>
<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
<label for="retype">{{.i18n.Tr "re_type"}}</label>
<input id="retype" name="retype" type="password" value="{{.retype}}" autocomplete="off" required>
</div>
{{end}}
{{if and .EnableCaptcha (eq .CaptchaType "image")}}
<div class="inline field">
<label></label>
Expand Down