make password and captcha optional for external registration

This commit is contained in:
AJ ONeal 2018-10-06 21:21:37 +00:00
parent 2e2eacf62a
commit 64971f88e6
5 changed files with 59 additions and 23 deletions

View File

@ -74,7 +74,7 @@ func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) bindin
type RegisterForm struct { type RegisterForm struct {
UserName string `binding:"Required;AlphaDashDot;MaxSize(35)"` UserName string `binding:"Required;AlphaDashDot;MaxSize(35)"`
Email string `binding:"Required;Email;MaxSize(254)"` Email string `binding:"Required;Email;MaxSize(254)"`
Password string `binding:"Required;MaxSize(255)"` Password string `binding:"MaxSize(255)"`
Retype string Retype string
GRecaptchaResponse string `form:"g-recaptcha-response"` GRecaptchaResponse string `form:"g-recaptcha-response"`
} }
@ -99,6 +99,7 @@ func (f *MustChangePasswordForm) Validate(ctx *macaron.Context, errs binding.Err
// SignInForm form for signing in with user/password // SignInForm form for signing in with user/password
type SignInForm struct { type SignInForm struct {
UserName string `binding:"Required;MaxSize(254)"` UserName string `binding:"Required;MaxSize(254)"`
// TODO remove required from password for SecondFactorAuthentication
Password string `binding:"Required;MaxSize(255)"` Password string `binding:"Required;MaxSize(255)"`
Remember bool Remember bool
} }

View File

@ -1208,6 +1208,8 @@ var Service struct {
EnableReverseProxyAuth bool EnableReverseProxyAuth bool
EnableReverseProxyAutoRegister bool EnableReverseProxyAutoRegister bool
EnableCaptcha bool EnableCaptcha bool
RequireExternalRegistrationCaptcha bool
RequireExternalRegistrationPassword bool
CaptchaType string CaptchaType string
RecaptchaSecret string RecaptchaSecret string
RecaptchaSitekey string RecaptchaSitekey string
@ -1237,6 +1239,8 @@ func newService() {
Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool() Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool() Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool(false) Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool(false)
Service.RequireExternalRegistrationCaptcha = sec.Key("REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA").MustBool()
Service.RequireExternalRegistrationPassword = sec.Key("REQUIRE_EXTERNAL_REGISTRATION_PASSWORD").MustBool()
Service.CaptchaType = sec.Key("CAPTCHA_TYPE").MustString(ImageCaptcha) Service.CaptchaType = sec.Key("CAPTCHA_TYPE").MustString(ImageCaptcha)
Service.RecaptchaSecret = sec.Key("RECAPTCHA_SECRET").MustString("") Service.RecaptchaSecret = sec.Key("RECAPTCHA_SECRET").MustString("")
Service.RecaptchaSitekey = sec.Key("RECAPTCHA_SITEKEY").MustString("") Service.RecaptchaSitekey = sec.Key("RECAPTCHA_SITEKEY").MustString("")

View File

@ -6,6 +6,8 @@
package user package user
import ( import (
"crypto/rand"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
@ -126,6 +128,7 @@ func SignIn(ctx *context.Context) {
ctx.Data["OAuth2Providers"] = oauth2Providers ctx.Data["OAuth2Providers"] = oauth2Providers
ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["Title"] = ctx.Tr("sign_in")
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
ctx.Data["AllowPassword"] = true
ctx.Data["PageIsSignIn"] = true ctx.Data["PageIsSignIn"] = true
ctx.Data["PageIsLogin"] = true ctx.Data["PageIsLogin"] = true
@ -145,6 +148,7 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) {
ctx.Data["OAuth2Providers"] = oauth2Providers ctx.Data["OAuth2Providers"] = oauth2Providers
ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["Title"] = ctx.Tr("sign_in")
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
ctx.Data["AllowPassword"] = true
ctx.Data["PageIsSignIn"] = true ctx.Data["PageIsSignIn"] = true
ctx.Data["PageIsLogin"] = true ctx.Data["PageIsLogin"] = true
@ -648,9 +652,10 @@ func oAuth2UserLoginCallback(loginSource *models.LoginSource, request *http.Requ
func LinkAccount(ctx *context.Context) { func LinkAccount(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("link_account") ctx.Data["Title"] = ctx.Tr("link_account")
ctx.Data["LinkAccountMode"] = true ctx.Data["LinkAccountMode"] = true
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha ctx.Data["EnableCaptcha"] = setting.Service.RequireExternalRegistrationCaptcha
ctx.Data["CaptchaType"] = setting.Service.CaptchaType ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["AllowPassword"] = setting.Service.RequireExternalRegistrationPassword && !setting.Service.AllowOnlyExternalRegistration
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false ctx.Data["ShowRegistrationButton"] = false
@ -675,9 +680,11 @@ func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) {
ctx.Data["Title"] = ctx.Tr("link_account") ctx.Data["Title"] = ctx.Tr("link_account")
ctx.Data["LinkAccountMode"] = true ctx.Data["LinkAccountMode"] = true
ctx.Data["LinkAccountModeSignIn"] = true ctx.Data["LinkAccountModeSignIn"] = true
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha ctx.Data["EnableCaptcha"] = setting.Service.RequireExternalRegistrationCaptcha
ctx.Data["CaptchaType"] = setting.Service.CaptchaType ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey 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["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false ctx.Data["ShowRegistrationButton"] = false
@ -743,9 +750,11 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
ctx.Data["Title"] = ctx.Tr("link_account") ctx.Data["Title"] = ctx.Tr("link_account")
ctx.Data["LinkAccountMode"] = true ctx.Data["LinkAccountMode"] = true
ctx.Data["LinkAccountModeRegister"] = true ctx.Data["LinkAccountModeRegister"] = true
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha ctx.Data["EnableCaptcha"] = setting.Service.RequireExternalRegistrationCaptcha
ctx.Data["CaptchaType"] = setting.Service.CaptchaType ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey 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["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false ctx.Data["ShowRegistrationButton"] = false
@ -769,13 +778,13 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
return 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.Data["Err_Captcha"] = true
ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplLinkAccount, &form) ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplLinkAccount, &form)
return return
} }
if setting.Service.EnableCaptcha && setting.Service.CaptchaType == setting.ReCaptcha { if setting.Service.RequireExternalRegistrationCaptcha && setting.Service.CaptchaType == setting.ReCaptcha {
valid, _ := recaptcha.Verify(form.GRecaptchaResponse) valid, _ := recaptcha.Verify(form.GRecaptchaResponse)
if !valid { if !valid {
ctx.Data["Err_Captcha"] = true ctx.Data["Err_Captcha"] = true
@ -784,15 +793,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 { if setting.Service.AllowOnlyExternalRegistration || !setting.Service.RequireExternalRegistrationPassword {
ctx.Data["Err_Password"] = true // Generating a random password a stop-gap shim to get around the password requirement
ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplLinkAccount, &form) // Eventually the database should be changed to indicate "Second Factor" enabled accounts
return // that do not introduce the security vulnerabilities of a password
} bytes := make([]byte, 16)
if len(strings.TrimSpace(form.Password)) > 0 && len(form.Password) < setting.MinPasswordLength { _, err := rand.Read(bytes)
ctx.Data["Err_Password"] = true if nil != err {
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplLinkAccount, &form) ctx.ServerError("CreateUser", err)
return 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) loginSource, err := models.GetActiveOAuth2LoginSourceByName(gothUser.(goth.User).Provider)
@ -884,6 +907,8 @@ func SignUp(ctx *context.Context) {
ctx.Data["CaptchaType"] = setting.Service.CaptchaType ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["AllowPassword"] = true
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.HTML(200, tplSignUp) ctx.HTML(200, tplSignUp)
@ -900,6 +925,8 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
ctx.Data["CaptchaType"] = setting.Service.CaptchaType ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["AllowPassword"] = true
//Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true //Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
if !setting.Service.ShowRegistrationButton { if !setting.Service.ShowRegistrationButton {
ctx.Error(403) ctx.Error(403)

View File

@ -11,10 +11,12 @@
<label for="user_name">{{.i18n.Tr "home.uname_holder"}}</label> <label for="user_name">{{.i18n.Tr "home.uname_holder"}}</label>
<input id="user_name" name="user_name" value="{{.user_name}}" autofocus required> <input id="user_name" name="user_name" value="{{.user_name}}" autofocus required>
</div> </div>
{{if .AllowPassword}}
<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}"> <div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
<label for="password">{{.i18n.Tr "password"}}</label> <label for="password">{{.i18n.Tr "password"}}</label>
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required> <input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
</div> </div>
{{end}}
{{if not .LinkAccountMode}} {{if not .LinkAccountMode}}
<div class="inline field"> <div class="inline field">
<label></label> <label></label>

View File

@ -21,14 +21,16 @@
<label for="email">{{.i18n.Tr "email"}}</label> <label for="email">{{.i18n.Tr "email"}}</label>
<input id="email" name="email" type="email" value="{{.email}}" required> <input id="email" name="email" type="email" value="{{.email}}" required>
</div> </div>
<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}"> {{if .AllowPassword}}
<label for="password">{{.i18n.Tr "password"}}</label> <div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required> <label for="password">{{.i18n.Tr "password"}}</label>
</div> <input id="password" name="password" type="password" value="{{.password}}" autocomplete="off" required>
<div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}"> </div>
<label for="retype">{{.i18n.Tr "re_type"}}</label> <div class="required inline field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister))}}error{{end}}">
<input id="retype" name="retype" type="password" value="{{.retype}}" autocomplete="off" required> <label for="retype">{{.i18n.Tr "re_type"}}</label>
</div> <input id="retype" name="retype" type="password" value="{{.retype}}" autocomplete="off" required>
</div>
{{end}}
{{if and .EnableCaptcha (eq .CaptchaType "image")}} {{if and .EnableCaptcha (eq .CaptchaType "image")}}
<div class="inline field"> <div class="inline field">
<label></label> <label></label>