diff --git a/modules/auth/user_form.go b/modules/auth/user_form.go index 0c342df86..10067d3e0 100644 --- a/modules/auth/user_form.go +++ b/modules/auth/user_form.go @@ -74,7 +74,7 @@ func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) bindin type RegisterForm struct { UserName string `binding:"Required;AlphaDashDot;MaxSize(35)"` Email string `binding:"Required;Email;MaxSize(254)"` - Password string `binding:"Required;MaxSize(255)"` + Password string `binding:"MaxSize(255)"` Retype string } @@ -86,6 +86,7 @@ func (f *RegisterForm) Validate(ctx *macaron.Context, errs binding.Errors) bindi // 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 } diff --git a/modules/setting/setting.go b/modules/setting/setting.go index a5f4457f3..7ce50dcc9 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -1165,6 +1165,8 @@ var Service struct { EnableReverseProxyAuth bool EnableReverseProxyAutoRegister bool EnableCaptcha bool + RequireExternalRegistrationCaptcha bool + RequireExternalRegistrationPassword bool DefaultKeepEmailPrivate bool DefaultAllowCreateOrganization bool EnableTimetracking bool @@ -1190,6 +1192,8 @@ func newService() { 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.RequireExternalRegistrationCaptcha = sec.Key("REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA").MustBool() + Service.RequireExternalRegistrationPassword = sec.Key("REQUIRE_EXTERNAL_REGISTRATION_PASSWORD").MustBool() 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) diff --git a/routers/user/auth.go b/routers/user/auth.go index 71a063df3..0eb6fcc2a 100644 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -6,6 +6,8 @@ package user import ( + "crypto/rand" + "encoding/hex" "errors" "fmt" "net/http" @@ -123,6 +125,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 @@ -142,6 +145,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 @@ -641,7 +645,8 @@ 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 //&& setting.Service.EnableCaptcha + ctx.Data["AllowPassword"] = setting.Service.RequireExternalRegistrationPassword && !setting.Service.AllowOnlyExternalRegistration ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration ctx.Data["ShowRegistrationButton"] = false @@ -688,7 +693,9 @@ 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 //&& setting.Service.EnableCaptcha + // 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 @@ -754,7 +761,9 @@ 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 //&& setting.Service.EnableCaptcha + // 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 @@ -778,21 +787,35 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au return } - if setting.Service.EnableCaptcha && !cpt.VerifyReq(ctx.Req) { + if setting.Service.RequireExternalRegistrationCaptcha && !cpt.VerifyReq(ctx.Req) { 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) - 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) @@ -881,6 +904,8 @@ func SignUp(ctx *context.Context) { ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha + ctx.Data["AllowPassword"] = true + ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration ctx.HTML(200, tplSignUp) @@ -894,6 +919,8 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha + ctx.Data["AllowPassword"] = true + //Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true if !setting.Service.ShowRegistrationButton { ctx.Error(403) diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl index bf1cc055f..ae998f6d7 100644 --- a/templates/user/auth/signin_inner.tmpl +++ b/templates/user/auth/signin_inner.tmpl @@ -15,10 +15,12 @@ + {{if .AllowPassword}}