From b3c1584ec4a5bcba84a10cd9b6501d0e978c2457 Mon Sep 17 00:00:00 2001 From: Alexander Kavon Date: Mon, 22 Jan 2024 00:37:21 -0500 Subject: update db adapter, server, routes, and user model to be sqlboiler compatible --- src/db/db.go | 47 +++++++++---------------------- src/server/server.go | 4 +-- src/user/routes.go | 26 ++++++++++-------- src/user/secret.go | 55 ++++++++++++++++++++++++++++++++++++ src/user/user.go | 78 ---------------------------------------------------- 5 files changed, 84 insertions(+), 126 deletions(-) create mode 100644 src/user/secret.go delete mode 100644 src/user/user.go diff --git a/src/db/db.go b/src/db/db.go index d74c874..198b2be 100644 --- a/src/db/db.go +++ b/src/db/db.go @@ -2,23 +2,20 @@ package db import ( "context" - "fmt" + "database/sql" "log" - "strings" - "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" + "github.com/jackc/pgx/v5/stdlib" "gitlab.com/alexkavon/newsstand/src/conf" ) -type Database struct { - p *pgxpool.Pool +type Db struct { + pool *pgxpool.Pool } -type DbValues pgx.NamedArgs - -func NewDb(config *conf.Conf) *Database { +func NewDb(config *conf.Conf) *Db { pool, err := pgxpool.New(context.Background(), config.Db.Url) if err != nil { log.Fatal(err) @@ -30,36 +27,18 @@ func NewDb(config *conf.Conf) *Database { log.Fatal(err) } log.Println("Database connection pool created.", testquery) - return &Database{ - p: pool, - } + return &Db{pool} + } -func (d *Database) InsertTable(table string, columns []string, values DbValues) error { - columnstr := stringifyColumns(columns, "") - valuesstr := stringifyColumns(columns, "@") - query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s) RETURNING *", table, columnstr, valuesstr) - log.Println(query, values) - // Convert DbValues to pgx.NamedArgs type to use NamedArgs type features - namedArgs := pgx.NamedArgs(values) - // TODO Use r.Context() from HTTP Request? - ctx := context.Background() - row, err := d.p.Exec(ctx, query, namedArgs) - if err != nil { - return err - } - log.Println("Row", row) - return nil +func (d *Db) Conn() *pgxpool.Pool { + return d.pool } -func (d *Database) Close() { - d.p.Close() +func (d *Db) ToSqlDb() *sql.DB { + return stdlib.OpenDBFromPool(d.pool) } -func stringifyColumns(columns []string, prefix string) string { - ncolumns := []string{} - for _, v := range columns { - ncolumns = append(ncolumns, fmt.Sprintf("%s%s", prefix, v)) - } - return strings.Join(ncolumns, ", ") +func (d *Db) Close() { + d.pool.Close() } diff --git a/src/server/server.go b/src/server/server.go index d883d3f..cd3e6b6 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -10,12 +10,12 @@ import ( type Server struct { Router *chi.Mux - Db *db.Database + Db *db.Db Config *conf.Conf Ui Ui } -func NewServer(config *conf.Conf, database *db.Database) *Server { +func NewServer(config *conf.Conf, database *db.Db) *Server { return &Server{ Router: NewRouter(config), Db: database, diff --git a/src/user/routes.go b/src/user/routes.go index d8c8d43..b163fb7 100644 --- a/src/user/routes.go +++ b/src/user/routes.go @@ -5,6 +5,8 @@ import ( "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" "gitlab.com/alexkavon/newsstand/src/sessions" ) @@ -64,33 +66,33 @@ func Store(s *server.Server) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { r.ParseForm() - user := &User{ - Db: s.Db, - Username: r.PostFormValue("username"), - Secret: r.PostFormValue("secret"), - Email: r.PostFormValue("email"), - } + var user models.User + 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.Println("Validator failed", err.(validator.ValidationErrors)) + log.Fatal("Validator failed", err.(validator.ValidationErrors)) } // Hash secret - err = user.HashSecret() + secret := &Secret{Raw: user.Secret} + err = secret.HashSecret() if err != nil { - log.Println("Hash failure", err) + log.Fatal("Hash failure", err) } + user.Secret = secret.Hash() // Store user - err = user.Insert() + err = user.Insert(r.Context(), s.Db.ToSqlDb(), boil.Infer()) if err != nil { - log.Println("Insert Error", err) + log.Fatal("Insert Error", err) } // Send email validation // Create cookie session - sessions.NewSession(w, sessions.SessionValues{"uid": user.Id, "username": user.Username}) + sessions.NewSession(w, sessions.SessionValues{"uid": user.ID, "username": user.Username}) // Redirect to user profile http.Redirect(w, r, "/u/me", http.StatusSeeOther) } diff --git a/src/user/secret.go b/src/user/secret.go new file mode 100644 index 0000000..55b8bc6 --- /dev/null +++ b/src/user/secret.go @@ -0,0 +1,55 @@ +package user + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + + "golang.org/x/crypto/argon2" +) + +type Secret struct { + Raw string + hash string +} + +func (s Secret) HashSecret() 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(s.Raw), + 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, + ) + s.hash = encodedHash + return err +} + +func (s *Secret) Hash() string { + return s.hash +} diff --git a/src/user/user.go b/src/user/user.go deleted file mode 100644 index d9fca6b..0000000 --- a/src/user/user.go +++ /dev/null @@ -1,78 +0,0 @@ -package user - -import ( - "crypto/rand" - "encoding/base64" - "fmt" - "time" - - "gitlab.com/alexkavon/newsstand/src/db" - "golang.org/x/crypto/argon2" -) - -type User struct { - Id int64 - Username string `validate:"required,max=50"` - Secret string `validate:"required,min=8,max=128"` - Email string `validate:"required,email"` - Karma uint64 - UpdatedAt time.Time - CreatedAt time.Time - hash string - Db *db.Database -} - -func NewUser(d *db.Database) *User { - return &User{ - Db: d, - } -} - -func (u *User) Insert() error { - err := u.Db.InsertTable( - "users", - []string{"username", "secret", "email"}, - db.DbValues{"username": u.Username, "secret": string(u.hash), "email": u.Email}, - ) - if err != nil { - return err - } - return nil -} - -func (u *User) HashSecret() 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.hash = encodedHash - return nil -} -- cgit v1.2.3