package main import ( "fmt" "net/http" "encoding/json" "crypto/rand" "reflect" "log" // muxer and form parser "github.com/gorilla/mux" "github.com/gorilla/schema" // error returns for HandlerFunc "github.com/creack/ehttp" // JWT stuff // "github.com/auth0/go-jwt-middleware" "github.com/dgrijalva/jwt-go" // form validation "github.com/asaskevich/govalidator" // 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"` 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 var err error decoder := schema.NewDecoder() err = r.ParseForm() if err != nil { 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", err) } db.First(&userRecord) if db.Error != nil { return ehttp.NewErrorf(http.StatusInternalServerError, "database lookup error", err) } 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", err) } log.Println("authenticated user", userRecord.Name, "with token", tokenString) jsonOut, _ := json.Marshal(map[string]string{"token": tokenString}) fmt.Fprint(w, string(jsonOut)) return nil } return ehttp.NewErrorf(http.StatusForbidden, "Could not find user/pass") } func register(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", err) } var input User err = decoder.Decode(&input, r.PostForm) if err != nil { return ehttp.NewErrorf(http.StatusInternalServerError, "could not decode user", err) } res, err := govalidator.ValidateStruct(input) if err != nil || res != true { return ehttp.NewErrorf(http.StatusBadRequest, "could not validate your data", err) } countName := 0; countEmail := 0 db.Model(&User{}).Where("name = ?", input.Name).Count(&countName) db.Model(&User{}).Where("email = ?", input.Email).Count(&countEmail) log.Println("have countName", countName, "mail", countEmail) if countName != 0 || countEmail != 0{ return ehttp.NewErrorf(http.StatusConflict, "username or email 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) 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 { return ehttp.NewErrorf(http.StatusInternalServerError, "could not create user", err) } db.Create(&input) log.Println("registered user", input.Name) return nil } func main() { var err error // TODO: maybe move to init()? db, err = gorm.Open("sqlite3", "test.db") if err != nil { log.Fatalln("cannot open db", err) } 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 r := mux.NewRouter() r.Handle("/getToken", ehttp.HandlerFunc(getToken)) r.Handle("/register", ehttp.HandlerFunc(register)) http.Handle("/", r) http.ListenAndServe(":8000", nil) }