summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--integrations/links_test.go4
-rw-r--r--options/locale/locale_en-US.ini1
-rw-r--r--routers/routes/routes.go7
-rw-r--r--routers/user/setting.go67
-rw-r--r--templates/user/settings/navbar.tmpl7
-rw-r--r--templates/user/settings/password.tmpl41
-rw-r--r--templates/user/settings/security.tmpl79
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" .}}