aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Kavon <me+git@alexkavon.com>2024-01-22 14:56:36 -0500
committerAlexander Kavon <me+git@alexkavon.com>2024-01-22 14:56:36 -0500
commita1df83e8b5737a198a3fba4de23ca2c80828f623 (patch)
tree95ae9bc9a87b945e149373d9753c8e158f003a6f
parent4f15e271f541ecd525268efa40992e0f5c057e12 (diff)
added validation for user fields on sqlboiler.BeforeInsertHook, added hashing of secret before insert
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--src/user/hooks.go72
-rw-r--r--src/user/routes.go20
-rw-r--r--src/user/secret.go17
5 files changed, 85 insertions, 30 deletions
diff --git a/go.mod b/go.mod
index 60789bc..df22072 100644
--- a/go.mod
+++ b/go.mod
@@ -19,8 +19,10 @@ require (
)
require (
+ github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
+ github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/gofrs/uuid v4.2.0+incompatible // indirect
diff --git a/go.sum b/go.sum
index f80bbd9..1d3fb6a 100644
--- a/go.sum
+++ b/go.sum
@@ -81,6 +81,8 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0=
+github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@@ -148,6 +150,8 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
+github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
diff --git a/src/user/hooks.go b/src/user/hooks.go
new file mode 100644
index 0000000..0ab5ab0
--- /dev/null
+++ b/src/user/hooks.go
@@ -0,0 +1,72 @@
+package user
+
+import (
+ "context"
+ "crypto/rand"
+ "encoding/base64"
+ "fmt"
+
+ validation "github.com/go-ozzo/ozzo-validation/v4"
+ "github.com/go-ozzo/ozzo-validation/v4/is"
+ "github.com/volatiletech/sqlboiler/v4/boil"
+ "gitlab.com/alexkavon/newsstand/src/models"
+ "golang.org/x/crypto/argon2"
+)
+
+func init() {
+ models.AddUserHook(boil.BeforeInsertHook, validate)
+ // should always be last
+ models.AddUserHook(boil.BeforeInsertHook, hashSecret)
+}
+
+func validate(ctx context.Context, exec boil.ContextExecutor, u *models.User) error {
+ // validate user
+ err := validation.ValidateStruct(u,
+ validation.Field(&u.Username, validation.Required, validation.Length(3, 50)),
+ validation.Field(&u.Secret, validation.Required, validation.Length(8, 128)),
+ validation.Field(&u.Email, validation.Required, is.Email),
+ )
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func hashSecret(ctx context.Context, exec boil.ContextExecutor, u *models.User) error {
+ hashconf := &struct {
+ memory uint32
+ iterations uint32
+ parallelism uint8
+ keyLength uint32
+ saltLength uint32
+ }{64 * 1024, 3, 2, 12, 16}
+ salt := make([]byte, hashconf.saltLength)
+ _, err := rand.Read(salt)
+ if err != nil {
+ return err
+ }
+
+ hash := argon2.IDKey(
+ []byte(u.Secret),
+ salt,
+ hashconf.iterations,
+ hashconf.memory,
+ hashconf.parallelism,
+ hashconf.keyLength,
+ )
+ b64Salt := base64.RawStdEncoding.EncodeToString(salt)
+ b64Hash := base64.RawStdEncoding.EncodeToString(hash)
+ encodedHash := fmt.Sprintf(
+ "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s",
+ argon2.Version,
+ hashconf.memory,
+ hashconf.iterations,
+ hashconf.parallelism,
+ b64Salt,
+ b64Hash,
+ )
+
+ u.Secret = encodedHash
+ return nil
+}
diff --git a/src/user/routes.go b/src/user/routes.go
index b163fb7..7cbd3fb 100644
--- a/src/user/routes.go
+++ b/src/user/routes.go
@@ -4,7 +4,6 @@ import (
"log"
"net/http"
- "github.com/go-playground/validator/v10"
"github.com/volatiletech/sqlboiler/v4/boil"
"gitlab.com/alexkavon/newsstand/src/models"
"gitlab.com/alexkavon/newsstand/src/server"
@@ -70,23 +69,10 @@ func Store(s *server.Server) http.HandlerFunc {
user.Username = r.PostFormValue("username")
user.Secret = r.PostFormValue("secret")
user.Email = r.PostFormValue("email")
- // Validate User Input
- v := validator.New()
- err := v.Struct(user)
- if err != nil {
- log.Fatal("Validator failed", err.(validator.ValidationErrors))
- }
-
- // Hash secret
- secret := &Secret{Raw: user.Secret}
- err = secret.HashSecret()
- if err != nil {
- log.Fatal("Hash failure", err)
- }
- user.Secret = secret.Hash()
- // Store user
- err = user.Insert(r.Context(), s.Db.ToSqlDb(), boil.Infer())
+ // Store user, this package (user) will init
+ // a validation hook to check values and hash secret
+ err := user.Insert(r.Context(), s.Db.ToSqlDb(), boil.Infer())
if err != nil {
log.Fatal("Insert Error", err)
}
diff --git a/src/user/secret.go b/src/user/secret.go
index 55b8bc6..b6382fe 100644
--- a/src/user/secret.go
+++ b/src/user/secret.go
@@ -8,12 +8,7 @@ import (
"golang.org/x/crypto/argon2"
)
-type Secret struct {
- Raw string
- hash string
-}
-
-func (s Secret) HashSecret() error {
+func HashSecret(secret string) (string, error) {
hashconf := &struct {
memory uint32
iterations uint32
@@ -24,11 +19,11 @@ func (s Secret) HashSecret() error {
salt := make([]byte, hashconf.saltLength)
_, err := rand.Read(salt)
if err != nil {
- return err
+ return "", err
}
hash := argon2.IDKey(
- []byte(s.Raw),
+ []byte(secret),
salt,
hashconf.iterations,
hashconf.memory,
@@ -46,10 +41,6 @@ func (s Secret) HashSecret() error {
b64Salt,
b64Hash,
)
- s.hash = encodedHash
- return err
-}
-func (s *Secret) Hash() string {
- return s.hash
+ return encodedHash, nil
}