Compare commits

...

17 Commits

Author SHA1 Message Date
AJ ONeal 76fbfde40c Merge branch 'v1.5.1-coolaj86' of ssh://git.coolaj86.com:22042/coolaj86/gitroast into gitroast 2018-10-09 00:44:49 -06:00
AJ ONeal 20717e6139 add github signin to navbar 2018-10-09 06:33:19 +00:00
AJ ONeal fa737a887b Add Google Analytics config option 2018-10-09 06:24:29 +00:00
AJ ONeal e09a6248f1 Merge branch 'v1.5.1-coolaj86' into gitroast 2018-10-08 22:58:43 -06:00
AJ ONeal 1ff523881d use 'info' since password may or may not be allowed 2018-10-09 04:55:09 +00:00
AJ ONeal 0ae89454e1 Merge branch 'release/v1.5' into gitroast 2018-10-08 22:40:19 -06:00
AJ ONeal ce1739bd16 merge go fmt + bugfix allow current user password reset 2018-10-07 05:46:15 +00:00
AJ ONeal 4c7727c3ed allow current user to reset their own password 2018-10-07 05:36:05 +00:00
AJ ONeal 8718156545 treat register like sign in, even oauth 2018-10-07 05:33:29 +00:00
AJ ONeal eda51c2079 treat register like sign in, even oauth 2018-10-07 03:28:11 +00:00
AJ ONeal e64ff60c13 do not require password for external registration 2018-10-06 21:21:37 +00:00
AJ ONeal ee0fa3e58f handle specific error; fix missing err typo 2018-10-06 20:05:16 +00:00
AJ ONeal 5a42829dbd changes as per discussion 2018-10-06 16:37:27 +00:00
AJ ONeal 996c9cbab3 add active to tab body as well 2018-10-05 04:56:44 +00:00
AJ ONeal 95df7779ef use tabs to switch between account link flows 2018-10-05 04:45:24 +00:00
AJ ONeal 20eec9abd3 disambiguate fresh start from adding recovery options 2018-10-03 06:52:40 +00:00
AJ ONeal fa7fc43a13 Show either sign up OR sign in 2018-10-03 05:07:49 +00:00
10 changed files with 235 additions and 95 deletions

View File

@ -74,8 +74,9 @@ 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
Remember bool
}
// Validate valideates the fields
@ -86,6 +87,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
}

View File

@ -88,6 +88,9 @@ var (
AppDataPath string
AppWorkPath string
// User settings
GoogleAnalyticsID string
// Server settings
Protocol Scheme
Domain string
@ -695,6 +698,7 @@ func NewContext() {
sec := Cfg.Section("server")
AppName = Cfg.Section("").Key("APP_NAME").MustString("Gitea: Git with a cup of tea")
GoogleAnalyticsID = Cfg.Section("").Key("GOOGLE_ANALYTICS_ID").String()
Protocol = HTTP
if sec.Key("PROTOCOL").String() == "https" {
@ -1165,6 +1169,8 @@ var Service struct {
EnableReverseProxyAuth bool
EnableReverseProxyAutoRegister bool
EnableCaptcha bool
RequireExternalRegistrationCaptcha bool
RequireExternalRegistrationPassword bool
DefaultKeepEmailPrivate bool
DefaultAllowCreateOrganization bool
EnableTimetracking bool
@ -1190,6 +1196,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)

View File

@ -61,6 +61,9 @@ func NewFuncMap() []template.FuncMap {
"DisableGravatar": func() bool {
return setting.DisableGravatar
},
"GoogleAnalyticsID": func() string {
return setting.GoogleAnalyticsID
},
"ShowFooterTemplateLoadTime": func() bool {
return setting.ShowFooterTemplateLoadTime
},

View File

@ -9,7 +9,6 @@ sign_in_with = Sign In With
sign_out = Sign Out
sign_up = Register
link_account = Link Account
link_account_signin_or_signup = Sign in with existing credentials to link your existing account to this account. Or register a new one.
register = Register
website = Website
version = Version
@ -223,6 +222,12 @@ twofa_passcode_incorrect = Your passcode is incorrect. If you misplaced your dev
twofa_scratch_token_incorrect = Your scratch code is incorrect.
login_userpass = Sign In
login_openid = OpenID
oauth_signup_tab = Register New Account
oauth_signup_title = Add Account Recovery Info
oauth_signup_submit = Complete Account
oauth_signin_tab = Link to Existing Account
oauth_signin_title = Sign In to Authorize Linked Account
oauth_signin_submit = Link Account
openid_connect_submit = Connect
openid_connect_title = Connect to an existing account
openid_connect_desc = The chosen OpenID URI is unknown. Associate it with a new account here.

View File

@ -184,6 +184,10 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/^:type(issues|pulls)$", reqSignIn, user.Issues)
// ***** START: User *****
m.Group("/user", func() {
m.Get("/reset_password", user.ResetPasswd)
m.Post("/reset_password", user.ResetPasswdPost)
})
m.Group("/user", func() {
m.Get("/login", user.SignIn)
m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost)
@ -204,8 +208,6 @@ func RegisterRoutes(m *macaron.Macaron) {
}, openIDSignInEnabled)
m.Get("/sign_up", user.SignUp)
m.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost)
m.Get("/reset_password", user.ResetPasswd)
m.Post("/reset_password", user.ResetPasswdPost)
m.Group("/oauth2", func() {
m.Get("/:provider", user.SignInOAuth)
m.Get("/:provider/callback", user.SignInOAuthCallback)

View File

@ -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
@ -490,6 +494,36 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
return setting.AppSubURL + "/"
}
func handleRegister(ctx *context.Context, u *models.User, remember bool, obeyRedirect bool) {
// Auto-set admin for the only user.
if models.CountUsers() == 1 {
u.IsAdmin = true
u.IsActive = true
u.SetLastLogin()
if err := models.UpdateUserCols(u, "is_admin", "is_active", "last_login_unix"); err != nil {
ctx.ServerError("UpdateUser", err)
return
}
}
// Send confirmation email
if setting.Service.RegisterEmailConfirm && u.ID > 1 {
models.SendActivateAccountMail(ctx.Context, u)
ctx.Data["IsSendRegisterMail"] = true
ctx.Data["Email"] = u.Email
ctx.Data["ActiveCodeLives"] = base.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale.Language())
ctx.HTML(200, TplActivate)
if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
log.Error(4, "Set cache(MailResendLimit) fail: %v", err)
}
return
}
// Complete the signin without logging in again
handleSignInFull(ctx, u, remember, true)
}
// SignInOAuth handles the OAuth2 login buttons
func SignInOAuth(ctx *context.Context) {
provider := ctx.Params(":provider")
@ -641,7 +675,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
@ -655,8 +690,30 @@ func LinkAccount(ctx *context.Context) {
return
}
ctx.Data["user_name"] = gothUser.(goth.User).NickName
ctx.Data["email"] = gothUser.(goth.User).Email
uname := gothUser.(goth.User).NickName
email := gothUser.(goth.User).Email
ctx.Data["user_name"] = uname
ctx.Data["email"] = email
if len(email) != 0 {
u, err := models.GetUserByEmail(email)
if err != nil && !models.IsErrUserNotExist(err) {
ctx.ServerError("UserSignIn", err)
return
}
if u != nil {
ctx.Data["user_exists"] = true
}
} else if len(uname) != 0 {
u, err := models.GetUserByName(uname)
if err != nil && !models.IsErrUserNotExist(err) {
ctx.ServerError("UserSignIn", err)
return
}
if u != nil {
ctx.Data["user_exists"] = true
}
}
ctx.HTML(200, tplLinkAccount)
}
@ -666,7 +723,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
@ -732,7 +791,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
@ -756,21 +817,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)
@ -778,14 +853,20 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
ctx.ServerError("CreateUser", err)
}
// TODO LoginName should come from form.UserName... shouldn't it?
u := &models.User{
Name: form.UserName,
Email: form.Email,
Passwd: form.Password,
IsActive: !setting.Service.RegisterEmailConfirm,
LoginType: models.LoginOAuth2,
LoginSource: loginSource.ID,
LoginName: gothUser.(goth.User).UserID,
Name: form.UserName,
Email: form.Email,
Passwd: form.Password,
IsActive: !setting.Service.RegisterEmailConfirm,
}
// This will link the account in such a way that it cannot be removed
// TODO why is this different from normal linking?
if setting.Service.AllowOnlyExternalRegistration {
u.LoginType = models.LoginOAuth2
u.LoginSource = loginSource.ID
u.LoginName = gothUser.(goth.User).UserID
}
if err := models.CreateUser(u); err != nil {
@ -809,36 +890,19 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
}
log.Trace("Account created: %s", u.Name)
// Auto-set admin for the only user.
if models.CountUsers() == 1 {
u.IsAdmin = true
u.IsActive = true
u.SetLastLogin()
if err := models.UpdateUserCols(u, "is_admin", "is_active", "last_login_unix"); err != nil {
ctx.ServerError("UpdateUser", err)
// This will link the account in such a way that it can be removed
if !setting.Service.AllowOnlyExternalRegistration {
err = models.LinkAccountToUser(u, gothUser.(goth.User))
if err != nil {
ctx.ServerError("UserLinkAccount", err)
return
}
}
// Send confirmation email
if setting.Service.RegisterEmailConfirm && u.ID > 1 {
models.SendActivateAccountMail(ctx.Context, u)
ctx.Data["IsSendRegisterMail"] = true
ctx.Data["Email"] = u.Email
ctx.Data["ActiveCodeLives"] = base.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale.Language())
ctx.HTML(200, TplActivate)
if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
log.Error(4, "Set cache(MailResendLimit) fail: %v", err)
}
return
}
ctx.Redirect(setting.AppSubURL + "/user/login")
handleRegister(ctx, u, form.Remember, true)
}
// SignOut sign out from login status
func SignOut(ctx *context.Context) {
func handleSignOut(ctx *context.Context) {
ctx.Session.Delete("uid")
ctx.Session.Delete("uname")
ctx.Session.Delete("socialId")
@ -848,6 +912,11 @@ func SignOut(ctx *context.Context) {
ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
ctx.SetCookie("lang", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true) // Setting the lang cookie will trigger the middleware to reset the language ot previous state.
}
// SignOut sign out from login status
func SignOut(ctx *context.Context) {
handleSignOut(ctx)
ctx.Redirect(setting.AppSubURL + "/")
}
@ -859,6 +928,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)
@ -872,6 +943,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)
@ -927,32 +1000,7 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
}
log.Trace("Account created: %s", u.Name)
// Auto-set admin for the only user.
if models.CountUsers() == 1 {
u.IsAdmin = true
u.IsActive = true
u.SetLastLogin()
if err := models.UpdateUserCols(u, "is_admin", "is_active", "last_login_unix"); err != nil {
ctx.ServerError("UpdateUser", err)
return
}
}
// Send confirmation email, no need for social account.
if setting.Service.RegisterEmailConfirm && u.ID > 1 {
models.SendActivateAccountMail(ctx.Context, u)
ctx.Data["IsSendRegisterMail"] = true
ctx.Data["Email"] = u.Email
ctx.Data["ActiveCodeLives"] = base.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale.Language())
ctx.HTML(200, TplActivate)
if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
log.Error(4, "Set cache(MailResendLimit) fail: %v", err)
}
return
}
ctx.Redirect(setting.AppSubURL + "/user/login")
handleRegister(ctx, u, form.Remember, true)
}
// Activate render activate user page
@ -1100,6 +1148,8 @@ func ForgotPasswdPost(ctx *context.Context) {
func ResetPasswd(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("auth.reset_password")
// TODO for security and convenience, show the username / email here
code := ctx.Query("code")
if len(code) == 0 {
ctx.Error(404)
@ -1140,6 +1190,10 @@ func ResetPasswdPost(ctx *context.Context) {
ctx.ServerError("UpdateUser", err)
return
}
// Just in case the user is signed in to another account
handleSignOut(ctx)
u.HashPassword(passwd)
if err := models.UpdateUserCols(u, "passwd", "rands", "salt"); err != nil {
ctx.ServerError("UpdateUser", err)
@ -1147,6 +1201,9 @@ func ResetPasswdPost(ctx *context.Context) {
}
log.Trace("User password reset: %s", u.Name)
// TODO change the former form to have password retype and remember me,
// then sign in here instead of redirecting
ctx.Redirect(setting.AppSubURL + "/user/login")
return
}

View File

@ -105,6 +105,17 @@
<script src="{{AppSubUrl}}/vendor/plugins/cssrelpreload/loadCSS.min.js"></script>
<script src="{{AppSubUrl}}/vendor/plugins/cssrelpreload/cssrelpreload.min.js"></script>
{{if .GoogleAnalyticsID}}
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id={{.GoogleAnalyticsID}}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{{GoogleAnalyticsID}}');
</script>
{{end}}
{{if .PageIsUserProfile}}
<meta property="og:title" content="{{.Owner.Name}}" />
<meta property="og:type" content="profile" />
@ -265,6 +276,9 @@
<a class="item{{if .PageIsSignIn}} active{{end}}" href="{{AppSubUrl}}/user/login?redirect_to={{.Link}}">
<i class="octicon octicon-sign-in"></i> {{.i18n.Tr "sign_in"}}
</a>
<a class="item" href="{{AppSubUrl}}/user/oauth2/GitHub?redirect_to={{.Link}}">
{{.i18n.Tr "sign_in_with"}} &nbsp;<i class="octicon octicon-mark-github"></i>
</a>
</div><!-- end anonymous right menu -->
{{end}}

View File

@ -1,15 +1,33 @@
{{template "base/head" .}}
<div class="user link-account">
<div class="ui middle very relaxed page grid">
<div class="column">
<p class="large center">
{{.i18n.Tr "link_account_signin_or_signup"}}
</p>
<div class="ui tabular menu">
<!-- TODO handle .ShowRegistrationButton once other login bugs are fixed -->
<div class="item {{if not .user_exists}}active{{end}}"
data-tab="auth-link-signup-tab">
{{.i18n.Tr "auth.oauth_signup_tab"}}
</div>
<div class="item {{if .user_exists}}active{{end}}"
data-tab="auth-link-signin-tab">
{{.i18n.Tr "auth.oauth_signin_tab"}}
</div>
</div>
<div class="ui tab {{if not .user_exists}}active{{end}}"
data-tab="auth-link-signup-tab">
{{template "user/auth/signup_inner" .}}
</div>
<div class="ui tab {{if .user_exists}}active{{end}}"
data-tab="auth-link-signin-tab">
<div class="ui user signin container icon">
{{template "user/auth/signin_inner" .}}
</div>
</div>
</div>
</div>
</div>
<div class="ui user signin container icon">
{{template "user/auth/signin_inner" .}}
</div>
{{template "user/auth/signup_inner" .}}
{{template "base/footer" .}}

View File

@ -2,7 +2,11 @@
{{template "base/alert" .}}
{{end}}
<h4 class="ui top attached header center">
{{.i18n.Tr "auth.login_userpass"}}
{{if .LinkAccountMode}}
{{.i18n.Tr "auth.oauth_signin_title"}}
{{else}}
{{.i18n.Tr "auth.login_userpass"}}
{{end}}
</h4>
<div class="ui attached segment">
<form class="ui form" action="{{.SignInLink}}" method="post">
@ -11,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>
@ -27,7 +33,13 @@
<div class="inline field">
<label></label>
<button class="ui green button">{{.i18n.Tr "sign_in"}}</button>
<button class="ui green button">
{{if .LinkAccountMode}}
{{.i18n.Tr "auth.oauth_signin_submit"}}
{{else}}
{{.i18n.Tr "sign_in"}}
{{end}}
</button>
<a href="{{AppSubUrl}}/user/forgot_password">{{.i18n.Tr "auth.forgot_password"}}</a>
</div>

View File

@ -4,7 +4,11 @@
<form class="ui form" action="{{.SignUpLink}}" method="post">
{{.CsrfTokenHtml}}
<h3 class="ui top attached header">
{{.i18n.Tr "sign_up"}}
{{if .LinkAccountMode}}
{{.i18n.Tr "auth.oauth_signup_title"}}
{{else}}
{{.i18n.Tr "sign_up"}}
{{end}}
</h3>
<div class="ui attached segment">
{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister)}}
@ -21,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 .EnableCaptcha}}
<div class="inline field">
<label></label>
@ -42,7 +48,20 @@
<div class="inline field">
<label></label>
<button class="ui green button">{{.i18n.Tr "auth.create_new_account"}}</button>
<div class="ui checkbox">
<label>{{.i18n.Tr "auth.remember_me"}}</label>
<input name="remember" type="checkbox">
</div>
</div>
<div class="inline field">
<label></label>
<button class="ui green button">
{{if .LinkAccountMode}}
{{.i18n.Tr "auth.oauth_signup_submit"}}
{{else}}
{{.i18n.Tr "auth.create_new_account"}}
{{end}}
</button>
</div>
{{if not .LinkAccountMode}}