tfstated/pkg/webui/accountsIdResetPassword.go
Julien Dessaux 5d7b540718
All checks were successful
main / main (push) Successful in 1m59s
main / deploy (push) Has been skipped
main / publish (push) Has been skipped
feat(webui): add csrf tokens to all forms processing code
Closes #60
2025-05-01 08:39:20 +02:00

95 lines
2.6 KiB
Go

package webui
import (
"fmt"
"html/template"
"net/http"
"git.adyxax.org/adyxax/tfstated/pkg/database"
"git.adyxax.org/adyxax/tfstated/pkg/model"
"go.n16f.net/uuid"
)
type AccountsIdResetPasswordPage struct {
Account *model.Account
Page *Page
PasswordInvalid bool
PasswordChanged bool
Token string
}
var accountsIdResetPasswordTemplates = template.Must(template.ParseFS(htmlFS, "html/base.html", "html/accountsIdResetPassword.html"))
func processAccountsIdResetPasswordPathValues(db *database.DB, w http.ResponseWriter, r *http.Request) *model.Account {
var accountId uuid.UUID
if err := accountId.Parse(r.PathValue("id")); err != nil {
return nil
}
var token uuid.UUID
if err := token.Parse(r.PathValue("token")); err != nil {
errorResponse(w, r, http.StatusBadRequest, err)
return nil
}
account, err := db.LoadAccountById(&accountId)
if err != nil {
errorResponse(w, r, http.StatusInternalServerError, err)
return nil
}
if account == nil || account.PasswordReset == nil {
errorResponse(w, r, http.StatusBadRequest, err)
return nil
}
if !account.PasswordReset.Equal(token) {
errorResponse(w, r, http.StatusBadRequest, err)
return nil
}
return account
}
func handleAccountsIdResetPasswordGET(db *database.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
account := processAccountsIdResetPasswordPathValues(db, w, r)
if account == nil {
return
}
render(w, accountsIdResetPasswordTemplates, http.StatusOK,
AccountsIdResetPasswordPage{
Account: account,
Page: makePage(r, &Page{Title: "Password Reset", Section: "reset"}),
Token: r.PathValue("token"),
})
})
}
func handleAccountsIdResetPasswordPOST(db *database.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
account := processAccountsIdResetPasswordPathValues(db, w, r)
if account == nil {
return
}
if err := r.ParseForm(); err != nil {
errorResponse(w, r, http.StatusBadRequest,
fmt.Errorf("failed to parse form: %w", err))
return
}
if !verifyCSRFToken(w, r) {
return
}
password := r.FormValue("password")
if len(password) < 8 {
errorResponse(w, r, http.StatusBadRequest, nil)
return
}
account.SetPassword(password)
if err := db.SaveAccount(account); err != nil {
errorResponse(w, r, http.StatusInternalServerError, err)
return
}
render(w, accountsIdResetPasswordTemplates, http.StatusOK,
AccountsIdResetPasswordPage{
Account: account,
Page: makePage(r, &Page{Title: "Password Reset", Section: "reset"}),
PasswordChanged: true,
})
})
}