1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-04 18:19:54 +00:00

implement user facing password change page

similar to the admin password change, but with a few changes:
- require current password verification

the following still included:
- empty password check
- confirm password check
This commit is contained in:
Jackson 2023-11-27 14:55:46 +01:00 committed by Timur Ismagilov
parent 4629f39e99
commit 5f592acc55
4 changed files with 152 additions and 0 deletions

62
settings/settings.go Normal file
View File

@ -0,0 +1,62 @@
package settings
import (
"fmt"
"mime"
"net/http"
"reflect"
"github.com/bouncepaw/mycorrhiza/viewutil"
"github.com/bouncepaw/mycorrhiza/user"
"github.com/bouncepaw/mycorrhiza/util"
)
func handlerUserChangePassword(w http.ResponseWriter, rq *http.Request) {
u := user.FromRequest(rq)
// TODO: is there a better way?
if reflect.DeepEqual(u, user.EmptyUser()) || u == nil {
util.HTTP404Page(w, "404 page not found")
return
}
f := util.FormDataFromRequest(rq, []string{"current_password", "password", "password_confirm"})
currentPassword := f.Get("current_password")
if user.CredentialsOK(u.Name, currentPassword) {
password := f.Get("password")
passwordConfirm := f.Get("password_confirm")
// server side validation
if password == "" {
err := fmt.Errorf("passwords should not be empty")
f = f.WithError(err)
}
if password == passwordConfirm {
previousPassword := u.Password // for rollback
if err := u.ChangePassword(password); err != nil {
f = f.WithError(err)
} else {
if err := user.SaveUserDatabase(); err != nil {
u.Password = previousPassword
f = f.WithError(err)
} else {
http.Redirect(w, rq, "/", http.StatusSeeOther)
return
}
}
} else {
err := fmt.Errorf("passwords do not match")
f = f.WithError(err)
}
} else {
err := fmt.Errorf("incorrect password")
f = f.WithError(err)
}
if f.HasError() {
w.WriteHeader(http.StatusBadRequest)
}
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
changePasswordPage(viewutil.MetaFrom(w, rq), f, u)
}

47
settings/view.go Normal file
View File

@ -0,0 +1,47 @@
package settings
import (
"embed"
"github.com/bouncepaw/mycorrhiza/util"
"github.com/bouncepaw/mycorrhiza/viewutil"
"github.com/gorilla/mux"
"net/http"
"github.com/bouncepaw/mycorrhiza/user"
)
// TODO: translate untranslated strings
const settingsTranslationRu = `
{{define "change password"}}Change password{{end}}
{{define "confirm password"}}Confirm password{{end}}
{{define "current password"}}Current password{{end}}
{{define "non local password change"}}Non-local accounts cannot have their passwords changed.{{end}}
{{define "password"}}Password{{end}}
{{define "submit"}}Submit{{end}}
`
var (
//go:embed *.html
fs embed.FS
changePassowrdChain viewutil.Chain
)
func Init(rtr *mux.Router) {
rtr.HandleFunc("/change-password", handlerUserChangePassword).Methods(http.MethodGet, http.MethodPost)
changePassowrdChain = viewutil.CopyEnRuWith(fs, "view_change_password.html", settingsTranslationRu)
}
func changePasswordPage(meta viewutil.Meta, form util.FormData, u *user.User) {
viewutil.ExecutePage(meta, changePassowrdChain, changePasswordData{
BaseData: &viewutil.BaseData{},
Form: form,
U: u,
})
}
type changePasswordData struct {
*viewutil.BaseData
Form util.FormData
U *user.User
}

View File

@ -0,0 +1,37 @@
{{/* TODO: translate title? */}}
{{define "title"}}Change password{{end}}
{{define "body"}}
<main class="main-width form-wrap">
{{if .Form.HasError}}
<div class="notice notice--error">
<strong>{{template "error"}}:</strong>
{{.Form.Error}}
</div>
{{end}}
<h2>{{block "change password" .}}Change password{{end}}</h2>
{{if eq .U.Source "local"}}
<form action="/settings/change-password" method="post">
<div class="form-field">
<label for="current_pass">{{block "current password" .}}Current password{{end}}</label>
<input required type="password" autocomplete="current-password" id="current_pass" name="current_password">
<br>
<br>
<label for="pass">{{block "password" .}}Password{{end}}</label>
<input required type="password" autocomplete="new-password" id="pass" name="password">
<br>
<br>
<label for="pass_confirm">{{block "confirm password" .}}Confirm password{{end}}</label>
<input required type="password" autocomplete="new-password" id="pass_confirm" name="password_confirm">
</div>
<div class="form-field">
<input class="btn" type="submit" value='{{block "submit" .}}Submit{{end}}'>
</div>
</form>
{{else}}
<p>{{block "non local password change" .}}Non-local accounts cannot have their passwords changed.{{end}}</p>
{{end}}
</main>
{{end}}

View File

@ -7,6 +7,7 @@ import (
"net/url" "net/url"
"github.com/bouncepaw/mycorrhiza/admin" "github.com/bouncepaw/mycorrhiza/admin"
"github.com/bouncepaw/mycorrhiza/settings"
"github.com/bouncepaw/mycorrhiza/auth" "github.com/bouncepaw/mycorrhiza/auth"
"github.com/bouncepaw/mycorrhiza/backlinks" "github.com/bouncepaw/mycorrhiza/backlinks"
"github.com/bouncepaw/mycorrhiza/categories" "github.com/bouncepaw/mycorrhiza/categories"
@ -67,6 +68,11 @@ func Handler() http.Handler {
adminRouter := wikiRouter.PathPrefix("/admin").Subrouter() adminRouter := wikiRouter.PathPrefix("/admin").Subrouter()
adminRouter.Use(groupMiddleware("admin")) adminRouter.Use(groupMiddleware("admin"))
admin.Init(adminRouter) admin.Init(adminRouter)
settingsRouter := wikiRouter.PathPrefix("/settings").Subrouter()
// TODO: check if necessary?
//settingsRouter.Use(groupMiddleware("settings"))
settings.Init(settingsRouter)
} }
// Index page // Index page