Newer
Older
"github.com/jmoiron/sqlx"
_ "modernc.org/sqlite" // Imported for side effects: registers DB driver
)
//go:embed templates/*.tpl
var templateFS embed.FS
//go:embed static/*
var staticFS embed.FS
func httpError(w http.ResponseWriter, msg string, err error, status int) {
if err != nil {
msg += ": " + err.Error()
}
log.Println(msg)
http.Error(w, msg, status)
func hashPassword(ctx context.Context, db *sqlx.DB, user, 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, ":", user, ":", pass)
type authProvider struct {
db *sqlx.DB
}
func (a authProvider) Valid(ctx context.Context, user, pass string) (bool, error) {
query := `SELECT count(*) FROM users;`
var count int
err := a.db.GetContext(ctx, &count, query)
// 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, user, 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)
}
func logRequest(r *http.Request) {
log.Println("handling", r.Method, r.URL, "from", r.RemoteAddr, "by", auth.User(r))
}
db, err := sqlx.Open("sqlite", "vino.sqlite")
if err != nil {
log.Fatalln("can't open DB:", err)
}
defer db.Close()
err = initDB(db)
if err != nil {
log.Fatalln("can't initialize DB:", err)
}
http.HandleFunc("/favicon.ico", http.NotFound)
http.HandleFunc("/details/img", auth.Require(http.HandlerFunc(handler.img), ap))
http.HandleFunc("/details/", auth.Require(http.HandlerFunc(handler.details), ap))
http.HandleFunc("/", auth.Require(http.HandlerFunc(handler.index), ap))