Skip to content
Snippets Groups Projects
Commit cc63e9cb authored by gbe's avatar gbe
Browse files

Store hashed passwords

parent 87419b8f
No related branches found
No related tags found
No related merge requests found
package main
import (
"crypto/rand"
"embed"
"fmt"
"io/fs"
......@@ -15,7 +16,7 @@ var migrationFS embed.FS
func initDB(db *sqlx.DB) error {
// Create table tracking migration state
const query = `CREATE TABLE IF NOT EXISTS migrations (name TEXT UNIQUE);`
query := `CREATE TABLE IF NOT EXISTS migrations (name TEXT UNIQUE);`
_, err := db.Exec(query)
if err != nil {
......@@ -88,5 +89,18 @@ func initDB(db *sqlx.DB) error {
log.Println("it has", len(data), "bytes")
}
// Initialize persistent state like password salt
buf := make([]byte, 16)
_, err = rand.Read(buf)
if err != nil {
return err
}
query = `INSERT OR IGNORE INTO state VALUES ('pwsalt', ?)`
_, err = db.Exec(query, buf)
if err != nil {
return err
}
return nil
}
......@@ -2,13 +2,12 @@ package main
import (
"context"
"database/sql"
"crypto/sha256"
"embed"
"errors"
"fmt"
"log"
"net/http"
"github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"
_ "modernc.org/sqlite" // Imported for side effects: registers DB driver
......@@ -35,31 +34,59 @@ func httpError(w http.ResponseWriter, msg string, err error, status int) {
http.Error(w, msg, status)
}
func hashPassword(ctx context.Context, db *sqlx.DB, pass string) (string, error) {
// Look up password salt from DB and hash password with it
query := `SELECT val FROM state WHERE key = 'pwsalt'`
var salt []byte
err := db.GetContext(ctx, &salt, query)
if err != nil {
return "", err
}
h := sha256.New()
_, err = h.Write(salt)
if err != nil {
return "", err
}
fmt.Fprint(h, pass)
return fmt.Sprintf("%02x", h.Sum(nil)), nil
}
type authProvider struct {
db *sqlx.DB
}
func (a authProvider) Valid(ctx context.Context, user, pass string) (bool, error) {
query, args, err := squirrel.Select("password").
From("users").
Where(squirrel.Eq{"name": user}).
ToSql()
query := `SELECT count(*) FROM users;`
var count int
err := a.db.GetContext(ctx, &count, query)
if err != nil {
return false, err
}
var dbPass string
err = a.db.GetContext(ctx, &dbPass, query, args...)
if errors.Is(err, sql.ErrNoRows) {
// User not found isn't an error, it's just an invalid auth.
return false, nil
// If no entry in user DB, just allow everything, so that initial user creation works.
if count == 0 {
log.Println("user database empty, allowing access by", user, "regardless of password")
return true, nil
}
hashedPw, err := hashPassword(ctx, a.db, pass)
log.Printf("hashed pw for %s: %s", user, hashedPw)
query = `SELECT count(*) FROM users WHERE name = ? AND password = ?`
err = a.db.GetContext(ctx, &count, query, user, hashedPw)
if err != nil {
return false, err
}
if dbPass == pass {
if count == 1 {
return true, nil
}
......
CREATE TABLE state (
key TEXT,
val TEXT,
UNIQUE(key)
);
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment