diff --git a/server/main.go b/server/main.go index fe617cc..a59e96d 100644 --- a/server/main.go +++ b/server/main.go @@ -3,10 +3,10 @@ package main import ( "fmt" "net/http" - "log" "encoding/json" -// "errors" -// "os" + "crypto/rand" + "reflect" + "log" // muxer and form parser "github.com/gorilla/mux" @@ -25,17 +25,50 @@ import ( // database backend "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/sqlite" + + // password hashing + "golang.org/x/crypto/scrypt" ) type User struct { gorm.Model - Name string `valid:"alphanum,required"` - Pass string `valid:"runelength(8|999),required"` - Email string `valid:"email,required"` + + Name string `valid:"alphanum,required"` + Pass string `valid:"runelength(8|999),required"` + Salt string `valid:"optional"` + Email string `valid:"email,required"` + Lists []List +} + +type List struct { + gorm.Model + UserID uint + + Name string `valid:"alphanum,required"` + Icon string `valid:"alphanum,optional"` + Items []Item +} + +type Item struct { + gorm.Model + ListID uint + + Name string `valid:"alphanum,required"` + Amount float64 `valid:"optional"` + Bought bool `valid:"required"` + Unit Unit + UnitID uint +} + +type Unit struct { + gorm.Model + + Name string `valid:"alphanum,required"` } var db *gorm.DB + func getToken(w http.ResponseWriter, r *http.Request) error { var userInput User var userRecord User @@ -44,28 +77,30 @@ func getToken(w http.ResponseWriter, r *http.Request) error { decoder := schema.NewDecoder() err = r.ParseForm() if err != nil { - return ehttp.NewErrorf(http.StatusInternalServerError, "could not parse form") + return ehttp.NewErrorf(http.StatusInternalServerError, "could not parse form", err) } err = decoder.Decode(&userInput, r.PostForm) if err != nil { - return ehttp.NewErrorf(http.StatusInternalServerError, "could not decode user from form") + return ehttp.NewErrorf(http.StatusInternalServerError, "could not decode user from form", err) } - log.Println("getToken got name:", userInput.Name, "pass:", userInput.Pass) db.First(&userRecord) if db.Error != nil { - log.Println("db finding user had error",err) - return ehttp.NewErrorf(http.StatusInternalServerError, "database lookup error") + return ehttp.NewErrorf(http.StatusInternalServerError, "database lookup error", err) } - if userRecord.Name == userInput.Name && userRecord.Pass == userInput.Pass { + hash, err := scrypt.Key([]byte(userInput.Pass), []byte(userRecord.Salt), 16384, 8, 1, 32) + if err != nil { + return ehttp.NewErrorf(http.StatusInternalServerError, "cannot hash pass", err) + } + if userRecord.Name == userInput.Name && reflect.DeepEqual(hash, []byte(userRecord.Pass)) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "name":userRecord.Name, }) tokenString, err := token.SignedString([]byte("TODO")) if err != nil { - return ehttp.NewErrorf(http.StatusForbidden, "could not construct token") + return ehttp.NewErrorf(http.StatusForbidden, "could not construct token", err) } log.Println("authenticated user", userRecord.Name, "with token", tokenString) @@ -74,7 +109,6 @@ func getToken(w http.ResponseWriter, r *http.Request) error { return nil } - log.Println("getToken user not found") return ehttp.NewErrorf(http.StatusForbidden, "Could not find user/pass") } @@ -82,38 +116,49 @@ func register(w http.ResponseWriter, r *http.Request) error { decoder := schema.NewDecoder() err := r.ParseForm() if err != nil { - log.Println("could not parse form because", err) - return ehttp.NewErrorf(http.StatusInternalServerError, "could not parse form") + return ehttp.NewErrorf(http.StatusInternalServerError, "could not parse form", err) } var input User err = decoder.Decode(&input, r.PostForm) if err != nil { - log.Println("could not decode user because", err) - return ehttp.NewErrorf(http.StatusInternalServerError, "could not decode user") + return ehttp.NewErrorf(http.StatusInternalServerError, "could not decode user", err) } res, err := govalidator.ValidateStruct(input) if err != nil || res != true { - log.Println("user",input,"was invalid because", err) - return ehttp.NewErrorf(http.StatusBadRequest, "could not validate your data") + return ehttp.NewErrorf(http.StatusBadRequest, "could not validate your data", err) } + countName := 0; countEmail := 0 + db.Model(&User{Name:input.Name}).Count(&countName) + db.Model(&User{Email:input.Email}).Count(&countEmail) + if countName != 0 || countEmail != 0{ + return ehttp.NewErrorf(http.StatusConflict, "username already exists") + } + + salt := make([]byte, 64) + _, err = rand.Read(salt) + if err != nil { + return ehttp.NewErrorf(http.StatusInternalServerError, "could not generate salt", err) + } + input.Salt = string(salt) - log.Println("registered user", input) + hash, err := scrypt.Key([]byte(input.Pass), salt, 16384, 8, 1, 32) + if err != nil { + return ehttp.NewErrorf(http.StatusInternalServerError, "could not hash pass", err) + } + input.Pass = string(hash) ret := db.NewRecord(input) if ret != true { - log.Println("cannot register user",input,"cannot create record") - return ehttp.NewErrorf(http.StatusInternalServerError, "could not create user") + return ehttp.NewErrorf(http.StatusInternalServerError, "could not create user", err) } db.Create(&input) - log.Println("registered user", input) + log.Println("registered user", input.Name) return nil } - - func main() { var err error @@ -124,8 +169,15 @@ func main() { } db.LogMode(false) + // Create or update schemas … db.AutoMigrate(&User{}) + db.AutoMigrate(&List{}) + db.AutoMigrate(&Item{}) + db.AutoMigrate(&Unit{}) + db.Model(&User{}).Related(&List{}) + db.Model(&List{}).Related(&Item{}) + db.Model(&Item{}).Related(&Unit{}) defer db.Close() // define URL handlers @@ -136,3 +188,4 @@ func main() { http.Handle("/", r) http.ListenAndServe(":8000", nil) } +