go,tmpl: implement+activate validator
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
also ad initial password change: * switch the password field type to `password` * add a field for repeated password
This commit is contained in:
parent
ff87c35dd1
commit
96c0b53493
@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"git.dotya.ml/mirre-mt/pcmt/modules/validation"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/labstack/echo-contrib/session"
|
"github.com/labstack/echo-contrib/session"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
@ -73,6 +74,8 @@ func (a *App) SetServerSettings() {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.Validator = validation.New()
|
||||||
|
|
||||||
// TODO: add check for prometheus config setting.
|
// TODO: add check for prometheus config setting.
|
||||||
// if true {
|
// if true {
|
||||||
// // import "github.com/labstack/echo-contrib/prometheus"
|
// // import "github.com/labstack/echo-contrib/prometheus"
|
||||||
|
5
go.mod
5
go.mod
@ -5,6 +5,7 @@ go 1.20
|
|||||||
require (
|
require (
|
||||||
entgo.io/ent v0.12.3
|
entgo.io/ent v0.12.3
|
||||||
github.com/CAFxX/httpcompression v0.0.8
|
github.com/CAFxX/httpcompression v0.0.8
|
||||||
|
github.com/go-playground/validator/v10 v10.15.3
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/sessions v1.2.1
|
github.com/gorilla/sessions v1.2.1
|
||||||
github.com/labstack/echo-contrib v0.15.0
|
github.com/labstack/echo-contrib v0.15.0
|
||||||
@ -29,7 +30,10 @@ require (
|
|||||||
github.com/aymerick/douceur v0.2.0 // indirect
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
github.com/go-openapi/inflect v0.19.0 // indirect
|
github.com/go-openapi/inflect v0.19.0 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
github.com/google/go-cmp v0.5.9 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
github.com/gorilla/context v1.1.1 // indirect
|
github.com/gorilla/context v1.1.1 // indirect
|
||||||
@ -38,6 +42,7 @@ require (
|
|||||||
github.com/hashicorp/hcl/v2 v2.17.0 // indirect
|
github.com/hashicorp/hcl/v2 v2.17.0 // indirect
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/klauspost/compress v1.16.7 // indirect
|
github.com/klauspost/compress v1.16.7 // indirect
|
||||||
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||||
|
16
go.sum
16
go.sum
@ -25,8 +25,17 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
|||||||
github.com/fxamacker/cbor/v2 v2.2.1-0.20200511212021-28e39be4a84f/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
github.com/fxamacker/cbor/v2 v2.2.1-0.20200511212021-28e39be4a84f/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||||
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
|
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
|
||||||
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
|
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
|
||||||
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
|
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.15.3 h1:S+sSpunYjNPDuXkWbK+x+bA7iXiW296KG4dL3X7xUZo=
|
||||||
|
github.com/go-playground/validator/v10 v10.15.3/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
@ -71,6 +80,8 @@ github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8
|
|||||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||||
github.com/leanovate/gopter v0.2.5-0.20190402064358-634a59d12406 h1:+OUpk+IVvmKU0jivOVFGtOzA6U5AWFs8HE4DRzWLOUE=
|
github.com/leanovate/gopter v0.2.5-0.20190402064358-634a59d12406 h1:+OUpk+IVvmKU0jivOVFGtOzA6U5AWFs8HE4DRzWLOUE=
|
||||||
github.com/leanovate/gopter v0.2.5-0.20190402064358-634a59d12406/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8=
|
github.com/leanovate/gopter v0.2.5-0.20190402064358-634a59d12406/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8=
|
||||||
|
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||||
|
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/matthewhartstonge/argon2 v0.3.3 h1:38/hupgfzqO2UGxqXqmSqErE8KJvQnIxWWg7IXUqWgQ=
|
github.com/matthewhartstonge/argon2 v0.3.3 h1:38/hupgfzqO2UGxqXqmSqErE8KJvQnIxWWg7IXUqWgQ=
|
||||||
@ -108,9 +119,14 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
|||||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
@ -13,8 +13,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNoSession = errors.New("No session found, please log in")
|
ErrNoSession = errors.New("No session found, please log in")
|
||||||
ErrSessionExpired = errors.New("Session expired, log in again")
|
ErrSessionExpired = errors.New("Session expired, log in again")
|
||||||
|
ErrValidationFailed = errors.New("Check your input data")
|
||||||
)
|
)
|
||||||
|
|
||||||
func renderErrorPage(c echo.Context, status int, statusText, error string) error {
|
func renderErrorPage(c echo.Context, status int, statusText, error string) error {
|
||||||
|
@ -216,6 +216,7 @@ func CreateUser() echo.HandlerFunc { //nolint:gocognit
|
|||||||
data["flash"] = msg
|
data["flash"] = msg
|
||||||
data["form"] = uc
|
data["form"] = uc
|
||||||
p.Data = data
|
p.Data = data
|
||||||
|
p.User = u
|
||||||
|
|
||||||
return c.Render(
|
return c.Render(
|
||||||
http.StatusBadRequest,
|
http.StatusBadRequest,
|
||||||
@ -224,6 +225,15 @@ func CreateUser() echo.HandlerFunc { //nolint:gocognit
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.Validate(uc); err != nil {
|
||||||
|
return renderErrorPage(
|
||||||
|
c,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
http.StatusText(http.StatusBadRequest)+" - "+ErrValidationFailed.Error(),
|
||||||
|
err.Error(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var msg string
|
var msg string
|
||||||
|
|
||||||
usr, err := moduser.CreateUser(ctx, dbclient, uc.Email, uc.Username, uc.Password, uc.IsAdmin)
|
usr, err := moduser.CreateUser(ctx, dbclient, uc.Email, uc.Username, uc.Password, uc.IsAdmin)
|
||||||
@ -309,6 +319,15 @@ func ViewUser() echo.HandlerFunc {
|
|||||||
|
|
||||||
err := c.Bind(uid)
|
err := c.Bind(uid)
|
||||||
if err == nil { //nolint:dupl
|
if err == nil { //nolint:dupl
|
||||||
|
if err := c.Validate(uid); err != nil {
|
||||||
|
return renderErrorPage(
|
||||||
|
c,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
http.StatusText(http.StatusBadRequest)+" - make sure to pass in a proper UUID",
|
||||||
|
err.Error(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
usr, err := getUserByID(ctx, dbclient, uid.ID)
|
usr, err := getUserByID(ctx, dbclient, uid.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, moduser.ErrUserNotFound) { //nolint:gocritic
|
if errors.Is(err, moduser.ErrUserNotFound) { //nolint:gocritic
|
||||||
@ -636,6 +655,15 @@ func UpdateUser() echo.HandlerFunc { //nolint:gocognit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.Validate(uu); err != nil {
|
||||||
|
return renderErrorPage(
|
||||||
|
c,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
http.StatusText(http.StatusBadRequest)+" - "+ErrValidationFailed.Error(),
|
||||||
|
err.Error(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if usr.Username != uu.Username {
|
if usr.Username != uu.Username {
|
||||||
exists, err := moduser.UsernameExists(ctx, dbclient, uu.Username)
|
exists, err := moduser.UsernameExists(ctx, dbclient, uu.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -145,6 +145,15 @@ func SearchHIBP() echo.HandlerFunc {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.Validate(a); err != nil {
|
||||||
|
return renderErrorPage(
|
||||||
|
c,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
http.StatusText(http.StatusBadRequest)+" - "+ErrValidationFailed.Error(),
|
||||||
|
err.Error(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
breachNames, err := hibp.GetAllBreachesForAccount(a.Account)
|
breachNames, err := hibp.GetAllBreachesForAccount(a.Account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := "Error getting breaches for this account"
|
msg := "Error getting breaches for this account"
|
||||||
|
@ -123,6 +123,15 @@ func SigninPost(client *ent.Client) echo.HandlerFunc {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.Validate(cu); err != nil {
|
||||||
|
return renderErrorPage(
|
||||||
|
c,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
http.StatusText(http.StatusBadRequest)+" - "+ErrValidationFailed.Error(),
|
||||||
|
err.Error(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
loginFailed := "Login Failed!"
|
loginFailed := "Login Failed!"
|
||||||
|
|
||||||
ctx := context.WithValue(context.Background(), moduser.CtxKey{}, slogger)
|
ctx := context.WithValue(context.Background(), moduser.CtxKey{}, slogger)
|
||||||
|
@ -72,6 +72,15 @@ func SignupPost(client *ent.Client) echo.HandlerFunc {
|
|||||||
return c.Redirect(http.StatusFound, "/singup")
|
return c.Redirect(http.StatusFound, "/singup")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.Validate(cu); err != nil {
|
||||||
|
return renderErrorPage(
|
||||||
|
c,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
http.StatusText(http.StatusBadRequest)+" - "+ErrValidationFailed.Error(),
|
||||||
|
err.Error(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
username := cu.Username
|
username := cu.Username
|
||||||
email := cu.Email
|
email := cu.Email
|
||||||
passwd := cu.Password
|
passwd := cu.Password
|
||||||
|
@ -4,39 +4,40 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
type userSignin struct {
|
type userSignin struct {
|
||||||
Username string `form:"username" json:"username" validate:"required,username,gte=2"`
|
Username string `form:"username" json:"username" validate:"required,gte=2"`
|
||||||
Password string `form:"password" json:"password" validate:"required,password,gte=12"`
|
Password string `form:"password" json:"password" validate:"required,gte=20"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type userSignup struct {
|
type userSignup struct {
|
||||||
Username string `form:"username" json:"username" validate:"required,username,gte=2"`
|
Username string `form:"username" json:"username" validate:"required,gte=2"`
|
||||||
Email string `form:"email" json:"email" validate:"required,email"`
|
Email string `form:"email" json:"email" validate:"required,email,gte=3"`
|
||||||
Password string `form:"password" json:"password" validate:"required,password,gte=20"`
|
Password string `form:"password" json:"password" validate:"required,gte=20"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// this struct is also used on update by admins, which is why the password fields are omitempty.
|
// this struct is also used on update by admins, which is why the password fields are omitempty.
|
||||||
// when users finish setting up, admins can no longer change their passwords.
|
// when users finish setting up, admins can no longer change their passwords.
|
||||||
type userCreate struct {
|
type userCreate struct {
|
||||||
Username string `form:"username" json:"username" validate:"required,username,gte=2"`
|
Username string `form:"username" json:"username" validate:"required,gte=2"`
|
||||||
Email string `form:"email" json:"email" validate:"required,email"`
|
Email string `form:"email" json:"email" validate:"required,email,gte=3"`
|
||||||
Password string `form:"password" json:"password" validate:"omitempty,password,gte=20"`
|
Password string `form:"password" json:"password" validate:"omitempty,gte=20,eqfield=RepeatPassword"`
|
||||||
RepeatPassword string `form:"repeatPassword" json:"repeatPassword" validate:"omitempty,repeatPassword,gte=20"`
|
RepeatPassword string `form:"repeatPassword" json:"repeatPassword" validate:"omitempty,gte=20,eqfield=Password"`
|
||||||
IsAdmin bool `form:"isAdmin" json:"isAdmin" validate:"required,isAdmin"`
|
IsAdmin bool `form:"isAdmin" json:"isAdmin" validate:"omitempty"`
|
||||||
IsActive *bool `form:"isActive" json:"isActive" validate:"omitempty,isActive"`
|
IsActive *bool `form:"isActive" json:"isActive" validate:"omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type userID struct {
|
type userID struct {
|
||||||
ID string `param:"id" validate:"required,id"`
|
ID string `param:"id" validate:"required,uuid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type initPasswordChange struct {
|
type initPasswordChange struct {
|
||||||
NewPassword string `form:"new-password" validate:"required,new-password,gte=20"`
|
NewPassword string `form:"new-password" validate:"required,gte=20,eqfield=RepeatNewPassword"`
|
||||||
|
RepeatNewPassword string `form:"repeat-new-password" validate:"required,gte=20,eqfield=NewPassword"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type hibpSearch struct {
|
type hibpSearch struct {
|
||||||
Account string `form:"search" validate:"required,search,gt=2"`
|
Account string `form:"search" validate:"required,gt=2"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type hibpBreachDetail struct {
|
type hibpBreachDetail struct {
|
||||||
BreachName string `param:"name" validate:"required,name,gt=0"`
|
BreachName string `param:"name" validate:"required,gt=0"`
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,24 @@ func InitialPasswordChangePost() echo.HandlerFunc {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pw.NewPassword != pw.RepeatNewPassword {
|
||||||
|
return renderErrorPage(
|
||||||
|
c,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
http.StatusText(http.StatusBadRequest)+" - the passwords were not the same",
|
||||||
|
err.Error(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Validate(pw); err != nil {
|
||||||
|
return renderErrorPage(
|
||||||
|
c,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
http.StatusText(http.StatusBadRequest)+" - "+ErrValidationFailed.Error(),
|
||||||
|
err.Error(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
err = moduser.ChangePassFirstLogin(ctx, dbclient, u.ID, pw.NewPassword)
|
err = moduser.ChangePassFirstLogin(ctx, dbclient, u.ID, pw.NewPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger().Errorf("error changing initial user password: %q", err)
|
c.Logger().Errorf("error changing initial user password: %q", err)
|
||||||
|
@ -38,6 +38,15 @@ func ViewHIBP() echo.HandlerFunc {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.Validate(h); err != nil {
|
||||||
|
return renderErrorPage(
|
||||||
|
c,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
http.StatusText(http.StatusBadRequest)+" - "+ErrValidationFailed.Error(),
|
||||||
|
err.Error(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
ctx, ok := c.Get("sloggerCtx").(context.Context)
|
ctx, ok := c.Get("sloggerCtx").(context.Context)
|
||||||
if !ok {
|
if !ok {
|
||||||
ctx = context.WithValue(context.Background(), hibp.CtxKey{}, slogger)
|
ctx = context.WithValue(context.Background(), hibp.CtxKey{}, slogger)
|
||||||
|
36
modules/validation/validator.go
Normal file
36
modules/validation/validator.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2023 wanderer <a_mirre at utb dot cz>
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
package validation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validator defines a validator that can be used with the echo framework.
|
||||||
|
type Validator struct {
|
||||||
|
validator *validator.Validate
|
||||||
|
}
|
||||||
|
|
||||||
|
// New provides a new instance of type Validator, initialised with the default
|
||||||
|
// validator.
|
||||||
|
func New() *Validator {
|
||||||
|
return &Validator{
|
||||||
|
validator: validator.New(
|
||||||
|
validator.WithRequiredStructEnabled(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate implements echo framework's Validator interface.
|
||||||
|
func (v *Validator) Validate(a any) error {
|
||||||
|
if err := v.validator.Struct(a); err != nil {
|
||||||
|
// Optionally, you could return the error to give each route more control over the status code
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -37,8 +37,18 @@
|
|||||||
<span class="absolute" role="img" aria-label="password lock icon">
|
<span class="absolute" role="img" aria-label="password lock icon">
|
||||||
{{ template "svg-password.tmpl" }}
|
{{ template "svg-password.tmpl" }}
|
||||||
</span>
|
</span>
|
||||||
<input name="new-password" type="new-password"
|
<input name="new-password" type="password"
|
||||||
placeholder="New password" {{if and .Data.form .Data.form.Password}}value="{{.Data.form.Password}}"{{else}}{{end}}
|
placeholder="New password" {{if and .Data.form .Data.form.NewPassword}}value="{{.Data.form.NewPassword}}"{{else}}{{end}}
|
||||||
|
minlength=20
|
||||||
|
required
|
||||||
|
class="block w-full px-10 py-3 required:border-slate-500 dark:required:border-slate-300 required:border-3 valid:border text-gray-700 bg-white border rounded-lg dark:bg-gray-900 dark:text-gray-300 dark:valid:border-gray-600 focus:border-blue-400 dark:focus:border-blue-300 focus:ring-blue-300 focus:outline-none focus:ring focus:ring-opacity-40">
|
||||||
|
</div>
|
||||||
|
<div class="relative flex items-center">
|
||||||
|
<span class="absolute" role="img" aria-label="password lock icon">
|
||||||
|
{{ template "svg-password.tmpl" }}
|
||||||
|
</span>
|
||||||
|
<input name="repeat-new-password" type="password"
|
||||||
|
placeholder="New password repeated" {{if and .Data.form .Data.form.RepeatNewPassword}}value="{{.Data.form.RepeatNewPassword}}"{{else}}{{end}}
|
||||||
minlength=20
|
minlength=20
|
||||||
required
|
required
|
||||||
class="block w-full px-10 py-3 required:border-slate-500 dark:required:border-slate-300 required:border-3 valid:border text-gray-700 bg-white border rounded-lg dark:bg-gray-900 dark:text-gray-300 dark:valid:border-gray-600 focus:border-blue-400 dark:focus:border-blue-300 focus:ring-blue-300 focus:outline-none focus:ring focus:ring-opacity-40">
|
class="block w-full px-10 py-3 required:border-slate-500 dark:required:border-slate-300 required:border-3 valid:border text-gray-700 bg-white border rounded-lg dark:bg-gray-900 dark:text-gray-300 dark:valid:border-gray-600 focus:border-blue-400 dark:focus:border-blue-300 focus:ring-blue-300 focus:outline-none focus:ring focus:ring-opacity-40">
|
||||||
|
Loading…
Reference in New Issue
Block a user