diff options
-rw-r--r-- | integrations/links_test.go | 4 | ||||
-rw-r--r-- | options/locale/locale_en-US.ini | 1 | ||||
-rw-r--r-- | routers/routes/routes.go | 7 | ||||
-rw-r--r-- | routers/user/setting.go | 67 | ||||
-rw-r--r-- | templates/user/settings/navbar.tmpl | 7 | ||||
-rw-r--r-- | templates/user/settings/password.tmpl | 41 | ||||
-rw-r--r-- | templates/user/settings/security.tmpl | 79 |
7 files changed, 117 insertions, 89 deletions
diff --git a/integrations/links_test.go b/integrations/links_test.go index bab147bdd9..d9d647f4b9 100644 --- a/integrations/links_test.go +++ b/integrations/links_test.go @@ -71,11 +71,11 @@ func testLinksAsUser(userName string, t *testing.T) { "/user2?tab=activity", "/user/settings", "/user/settings/avatar", - "/user/settings/password", + "/user/settings/security", + "/user/settings/security/two_factor/enroll", "/user/settings/email", "/user/settings/keys", "/user/settings/applications", - "/user/settings/two_factor", "/user/settings/account_link", "/user/settings/organization", "/user/settings/delete", diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index fede39484c..f664827821 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -303,6 +303,7 @@ form.name_pattern_not_allowed = The username pattern '%s' is not allowed. [settings] profile = Profile password = Password +security = Security avatar = Avatar ssh_gpg_keys = SSH / GPG Keys social = Social Accounts diff --git a/routers/routes/routes.go b/routers/routes/routes.go index a6f73aaedd..749f8263f8 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -220,8 +220,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Combo("/email").Get(user.SettingsEmails). Post(bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost) m.Post("/email/delete", user.DeleteEmail) - m.Get("/password", user.SettingsPassword) - m.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost) + m.Get("/security", user.SettingsSecurity) + m.Post("/security", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsSecurityPost) m.Group("/openid", func() { m.Combo("").Get(user.SettingsOpenID). Post(bindIgnErr(auth.AddOpenIDForm{}), user.SettingsOpenIDPost) @@ -238,8 +238,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Combo("/account_link").Get(user.SettingsAccountLinks).Post(user.SettingsDeleteAccountLink) m.Get("/organization", user.SettingsOrganization) m.Get("/repos", user.SettingsRepos) - m.Group("/two_factor", func() { - m.Get("", user.SettingsTwoFactor) + m.Group("/security/two_factor", func() { m.Post("/regenerate_scratch", user.SettingsTwoFactorRegenerateScratch) m.Post("/disable", user.SettingsTwoFactorDisable) m.Get("/enroll", user.SettingsTwoFactorEnroll) diff --git a/routers/user/setting.go b/routers/user/setting.go index b71b29ba21..a00f3f287a 100644 --- a/routers/user/setting.go +++ b/routers/user/setting.go @@ -41,7 +41,7 @@ const ( tplSettingsOrganization base.TplName = "user/settings/organization" tplSettingsRepositories base.TplName = "user/settings/repos" tplSettingsDelete base.TplName = "user/settings/delete" - tplSecurity base.TplName = "user/security" + tplSettingsSecurity base.TplName = "user/settings/security" ) // Settings render user's profile page @@ -191,22 +191,35 @@ func SettingsDeleteAvatar(ctx *context.Context) { ctx.Redirect(setting.AppSubURL + "/user/settings/avatar") } -// SettingsPassword render change user's password page -func SettingsPassword(ctx *context.Context) { +// SettingsSecurity render change user's password page and 2FA +func SettingsSecurity(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsPassword"] = true + ctx.Data["PageIsSettingsSecurity"] = true ctx.Data["Email"] = ctx.User.Email - ctx.HTML(200, tplSettingsPassword) + + enrolled := true + _, err := models.GetTwoFactorByUID(ctx.User.ID) + if err != nil { + if models.IsErrTwoFactorNotEnrolled(err) { + enrolled = false + } else { + ctx.Handle(500, "SettingsTwoFactor", err) + return + } + } + + ctx.Data["TwofaEnrolled"] = enrolled + ctx.HTML(200, tplSettingsSecurity) } -// SettingsPasswordPost response for change user's password -func SettingsPasswordPost(ctx *context.Context, form auth.ChangePasswordForm) { +// SettingsSecurityPost response for change user's password +func SettingsSecurityPost(ctx *context.Context, form auth.ChangePasswordForm) { ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsPassword"] = true + ctx.Data["PageIsSettingsSecurity"] = true ctx.Data["PageIsSettingsDelete"] = true if ctx.HasError() { - ctx.HTML(200, tplSettingsPassword) + ctx.HTML(200, tplSettingsSecurity) return } @@ -230,7 +243,7 @@ func SettingsPasswordPost(ctx *context.Context, form auth.ChangePasswordForm) { ctx.Flash.Success(ctx.Tr("settings.change_password_success")) } - ctx.Redirect(setting.AppSubURL + "/user/settings/password") + ctx.Redirect(setting.AppSubURL + "/user/settings/security") } // SettingsEmails render user's emails page @@ -509,30 +522,10 @@ func SettingsDeleteApplication(ctx *context.Context) { }) } -// SettingsTwoFactor renders the 2FA page. -func SettingsTwoFactor(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsTwofa"] = true - - enrolled := true - _, err := models.GetTwoFactorByUID(ctx.User.ID) - if err != nil { - if models.IsErrTwoFactorNotEnrolled(err) { - enrolled = false - } else { - ctx.Handle(500, "SettingsTwoFactor", err) - return - } - } - - ctx.Data["TwofaEnrolled"] = enrolled - ctx.HTML(200, tplSettingsTwofa) -} - // SettingsTwoFactorRegenerateScratch regenerates the user's 2FA scratch code. func SettingsTwoFactorRegenerateScratch(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsTwofa"] = true + ctx.Data["PageIsSettingsSecurity"] = true t, err := models.GetTwoFactorByUID(ctx.User.ID) if err != nil { @@ -551,13 +544,13 @@ func SettingsTwoFactorRegenerateScratch(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("settings.twofa_scratch_token_regenerated", t.ScratchToken)) - ctx.Redirect(setting.AppSubURL + "/user/settings/two_factor") + ctx.Redirect(setting.AppSubURL + "/user/settings/security") } // SettingsTwoFactorDisable deletes the user's 2FA settings. func SettingsTwoFactorDisable(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsTwofa"] = true + ctx.Data["PageIsSettingsSecurity"] = true t, err := models.GetTwoFactorByUID(ctx.User.ID) if err != nil { @@ -571,7 +564,7 @@ func SettingsTwoFactorDisable(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("settings.twofa_disabled")) - ctx.Redirect(setting.AppSubURL + "/user/settings/two_factor") + ctx.Redirect(setting.AppSubURL + "/user/settings/security") } func twofaGenerateSecretAndQr(ctx *context.Context) bool { @@ -615,7 +608,7 @@ func twofaGenerateSecretAndQr(ctx *context.Context) bool { // SettingsTwoFactorEnroll shows the page where the user can enroll into 2FA. func SettingsTwoFactorEnroll(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsTwofa"] = true + ctx.Data["PageIsSettingsSecurity"] = true t, err := models.GetTwoFactorByUID(ctx.User.ID) if t != nil { @@ -638,7 +631,7 @@ func SettingsTwoFactorEnroll(ctx *context.Context) { // SettingsTwoFactorEnrollPost handles enrolling the user into 2FA. func SettingsTwoFactorEnrollPost(ctx *context.Context, form auth.TwoFactorAuthForm) { ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsTwofa"] = true + ctx.Data["PageIsSettingsSecurity"] = true t, err := models.GetTwoFactorByUID(ctx.User.ID) if t != nil { @@ -691,7 +684,7 @@ func SettingsTwoFactorEnrollPost(ctx *context.Context, form auth.TwoFactorAuthFo ctx.Session.Delete("twofaSecret") ctx.Session.Delete("twofaUri") ctx.Flash.Success(ctx.Tr("settings.twofa_enrolled", t.ScratchToken)) - ctx.Redirect(setting.AppSubURL + "/user/settings/two_factor") + ctx.Redirect(setting.AppSubURL + "/user/settings/security") } // SettingsAccountLinks render the account links settings page diff --git a/templates/user/settings/navbar.tmpl b/templates/user/settings/navbar.tmpl index b0a3c9fcc3..a6c497794f 100644 --- a/templates/user/settings/navbar.tmpl +++ b/templates/user/settings/navbar.tmpl @@ -5,8 +5,8 @@ <a class="{{if .PageIsSettingsAvatar}}active{{end}} item" href="{{AppSubUrl}}/user/settings/avatar"> {{.i18n.Tr "settings.avatar"}} </a> - <a class="{{if .PageIsSettingsPassword}}active{{end}} item" href="{{AppSubUrl}}/user/settings/password"> - {{.i18n.Tr "settings.password"}} + <a class="{{if .PageIsSettingsSecurity}}active{{end}} item" href="{{AppSubUrl}}/user/settings/security"> + {{.i18n.Tr "settings.security"}} </a> <a class="{{if .PageIsSettingsEmails}}active{{end}} item" href="{{AppSubUrl}}/user/settings/email"> {{.i18n.Tr "settings.emails"}} @@ -22,9 +22,6 @@ <a class="{{if .PageIsSettingsApplications}}active{{end}} item" href="{{AppSubUrl}}/user/settings/applications"> {{.i18n.Tr "settings.applications"}} </a> - <a class="{{if .PageIsSettingsTwofa}}active{{end}} item" href="{{AppSubUrl}}/user/settings/two_factor"> - {{.i18n.Tr "settings.twofa"}} - </a> <a class="{{if .PageIsSettingsAccountLink}}active{{end}} item" href="{{AppSubUrl}}/user/settings/account_link"> {{.i18n.Tr "settings.account_link"}} </a> diff --git a/templates/user/settings/password.tmpl b/templates/user/settings/password.tmpl deleted file mode 100644 index 53872c901d..0000000000 --- a/templates/user/settings/password.tmpl +++ /dev/null @@ -1,41 +0,0 @@ -{{template "base/head" .}} -<div class="user settings password"> - {{template "user/settings/navbar" .}} - <div class="ui container"> - {{template "base/alert" .}} - <h4 class="ui top attached header"> - {{.i18n.Tr "settings.change_password"}} - </h4> - <div class="ui attached segment"> - {{if or (.SignedUser.IsLocal) (.SignedUser.IsOAuth2)}} - <form class="ui form" action="{{.Link}}" method="post"> - {{.CsrfTokenHtml}} - {{if .SignedUser.IsPasswordSet}} - <div class="required field {{if .Err_OldPassword}}error{{end}}"> - <label for="old_password">{{.i18n.Tr "settings.old_password"}}</label> - <input id="old_password" name="old_password" type="password" autocomplete="off" autofocus required> - </div> - {{end}} - <div class="required field {{if .Err_Password}}error{{end}}"> - <label for="password">{{.i18n.Tr "settings.new_password"}}</label> - <input id="password" name="password" type="password" autocomplete="off" required> - </div> - <div class="required field {{if .Err_Password}}error{{end}}"> - <label for="retype">{{.i18n.Tr "settings.retype_new_password"}}</label> - <input id="retype" name="retype" type="password" autocomplete="off" required> - </div> - - <div class="field"> - <button class="ui green button">{{$.i18n.Tr "settings.change_password"}}</button> - <a href="{{AppSubUrl}}/user/forgot_password?email={{.Email}}">{{.i18n.Tr "auth.forgot_password"}}</a> - </div> - </form> - {{else}} - <div class="ui info message"> - <p class="text left">{{$.i18n.Tr "settings.password_change_disabled"}}</p> - </div> - {{end}} - </div> - </div> -</div> -{{template "base/footer" .}} diff --git a/templates/user/settings/security.tmpl b/templates/user/settings/security.tmpl new file mode 100644 index 0000000000..a956a3fcb3 --- /dev/null +++ b/templates/user/settings/security.tmpl @@ -0,0 +1,79 @@ +{{template "base/head" .}} +<div class="user settings password"> + {{template "user/settings/navbar" .}} + <div class="ui container"> + {{template "base/alert" .}} + <h4 class="ui top attached header"> + {{.i18n.Tr "settings.password"}} + </h4> + <div class="ui attached segment"> + {{if or (.SignedUser.IsLocal) (.SignedUser.IsOAuth2)}} + <form class="ui form" action="{{.Link}}?tp=password" method="post"> + {{.CsrfTokenHtml}} + {{if .SignedUser.IsPasswordSet}} + <div class="required field {{if .Err_OldPassword}}error{{end}}"> + <label for="old_password">{{.i18n.Tr "settings.old_password"}}</label> + <input id="old_password" name="old_password" type="password" autocomplete="off" autofocus required> + </div> + {{end}} + <div class="required field {{if .Err_Password}}error{{end}}"> + <label for="password">{{.i18n.Tr "settings.new_password"}}</label> + <input id="password" name="password" type="password" autocomplete="off" required> + </div> + <div class="required field {{if .Err_Password}}error{{end}}"> + <label for="retype">{{.i18n.Tr "settings.retype_new_password"}}</label> + <input id="retype" name="retype" type="password" autocomplete="off" required> + </div> + + <div class="field"> + <button class="ui green button">{{$.i18n.Tr "settings.change_password"}}</button> + <a href="{{AppSubUrl}}/user/forgot_password?email={{.Email}}">{{.i18n.Tr "auth.forgot_password"}}</a> + </div> + </form> + {{else}} + <div class="ui info message"> + <p class="text left">{{$.i18n.Tr "settings.password_change_disabled"}}</p> + </div> + {{end}} + </div> + <br/> + + <h4 class="ui top attached header"> + {{.i18n.Tr "settings.twofa"}} + </h4> + <div class="ui attached segment"> + <p>{{.i18n.Tr "settings.twofa_desc"}}</p> + {{if .TwofaEnrolled}} + <p>{{$.i18n.Tr "settings.twofa_is_enrolled" | Str2html }}</p> + <form class="ui form" action="{{.Link}}/two_factor/regenerate_scratch" method="post" enctype="multipart/form-data"> + {{.CsrfTokenHtml}} + <p>{{.i18n.Tr "settings.regenerate_scratch_token_desc"}}</p> + <button class="ui blue button">{{$.i18n.Tr "settings.twofa_scratch_token_regenerate"}}</button> + </form> + <form class="ui form" action="{{.Link}}/two_factor/disable" method="post" enctype="multipart/form-data" id="disable-form"> + {{.CsrfTokenHtml}} + <p>{{.i18n.Tr "settings.twofa_disable_note"}}</p> + <div class="ui red button delete-button" data-type="form" data-form="#disable-form">{{$.i18n.Tr "settings.twofa_disable"}}</div> + </form> + {{else}} + <p>{{.i18n.Tr "settings.twofa_not_enrolled"}}</p> + <div class="inline field"> + <a class="ui green button" href="{{.Link}}/two_factor/enroll">{{$.i18n.Tr "settings.twofa_enroll"}}</a> + </div> + {{end}} + </div> + </div> +</div> + +<div class="ui small basic delete modal"> + <div class="ui icon header"> + <i class="trash icon"></i> + {{.i18n.Tr "settings.twofa_disable"}} + </div> + <div class="content"> + <p>{{.i18n.Tr "settings.twofa_disable_desc"}}</p> + </div> + {{template "base/delete_modal_actions" .}} +</div> + +{{template "base/footer" .}} |