package routes import ( "crypto/rand" "database/sql" "html/template" "net/http" "net/url" "shelves/backend/auth" "shelves/backend/errorsx" "shelves/backend/forms" "shelves/backend/httpx" "shelves/backend/templates" ) func setupRenderView(f setupForm, e setupFormErrors) template.HTML { body := templates.Form{ Action: "/setup", Fields: []templates.Field{ templates.Field{ Label: "Display name", Name: "displayName", Placeholder: "Jane Doe", Value: f.displayName, Error: errorsx.String(e.displayName), }, templates.Field{ Label: "Password", Type: "password", Placeholder: "Password", Name: "password", Error: errorsx.String(e.password), }, templates.Field{ Label: "Confirm password", Placeholder: "Confirm Password", Type: "password", Name: "passwordConfirmation", }, }, } return templates.PageBase{Title: "Set up", Body: body.HTML()}.HTML() } func queryGetOwnerSettings(db *sql.DB) (setupForm, error) { form := setupForm{} err := db.QueryRow(`select display_name from owner_settings`).Scan(&form.displayName) if err == sql.ErrNoRows { err = nil } return form, err } func SetupGet(w http.ResponseWriter, r *http.Request) { ctx := httpx.GetCtx(r) if !ctx.Auth.IsAdmin && !ctx.NeedsOwnerSetup { httpx.LoginRedirect(w, *r.URL) return } form, err := queryGetOwnerSettings(ctx.DB) if err != nil { httpx.InternalServerError(w, err) return } html(w, setupRenderView(form, setupFormErrors{})) } type setupForm struct { password string displayName string } type setupFormErrors struct { password error displayName error } func parseForm(f *setupForm, e *setupFormErrors, vs url.Values, v *forms.Validator) { f.password = vs.Get("password") passwordConfirmation := vs.Get("passwordConfirmation") f.displayName = vs.Get("displayName") e.password = v.MinLength(f.password, 1) if e.password == nil && f.password != passwordConfirmation { e.password = v.Fail("Passwords did not match") } e.displayName = v.MinLength(f.displayName, 1) } func queryUpdateOwnerSettings(db *sql.DB, display_name string, password_salt []byte, password_hash []byte) error { _, err := db.Exec(` insert or replace into owner_settings (display_name, password_salt, password_hash) values (?, ?, ?) `, display_name, password_salt, password_hash) return err } func updateOwnerSettings(db *sql.DB, f setupForm) error { salt := make([]byte, 32) _, err := rand.Read(salt) if err != nil { return err } hash := auth.HashPassword([]byte(f.password), salt) return queryUpdateOwnerSettings(db, f.displayName, salt, hash) } func SetupPost(w http.ResponseWriter, r *http.Request) { ctx := httpx.GetCtx(r) if !ctx.Auth.IsAdmin && !ctx.NeedsOwnerSetup { httpx.Unauthorized(w) return } form := setupForm{} errs := setupFormErrors{} failed := forms.ParseFormData(r, func(vs url.Values, v *forms.Validator) { parseForm(&form, &errs, vs, v) }) if failed { w.WriteHeader(http.StatusBadRequest) html(w, setupRenderView(form, errs)) return } err := updateOwnerSettings(ctx.DB, form) if err != nil { httpx.InternalServerError(w, err) } else { httpx.SeeOther(w, "/") } }