|
|
|
@ -3,10 +3,10 @@ package main
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/http"
|
|
|
|
"log"
|
|
|
|
|
|
|
|
"encoding/json"
|
|
|
|
"encoding/json"
|
|
|
|
// "errors"
|
|
|
|
"crypto/rand"
|
|
|
|
// "os"
|
|
|
|
"reflect"
|
|
|
|
|
|
|
|
"log"
|
|
|
|
|
|
|
|
|
|
|
|
// muxer and form parser
|
|
|
|
// muxer and form parser
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
@ -25,17 +25,50 @@ import (
|
|
|
|
// database backend
|
|
|
|
// database backend
|
|
|
|
"github.com/jinzhu/gorm"
|
|
|
|
"github.com/jinzhu/gorm"
|
|
|
|
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
|
|
|
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// password hashing
|
|
|
|
|
|
|
|
"golang.org/x/crypto/scrypt"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type User struct {
|
|
|
|
type User struct {
|
|
|
|
gorm.Model
|
|
|
|
gorm.Model
|
|
|
|
|
|
|
|
|
|
|
|
Name string `valid:"alphanum,required"`
|
|
|
|
Name string `valid:"alphanum,required"`
|
|
|
|
Pass string `valid:"runelength(8|999),required"`
|
|
|
|
Pass string `valid:"runelength(8|999),required"`
|
|
|
|
|
|
|
|
Salt string `valid:"optional"`
|
|
|
|
Email string `valid:"email,required"`
|
|
|
|
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
|
|
|
|
var db *gorm.DB
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func getToken(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
func getToken(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
var userInput User
|
|
|
|
var userInput User
|
|
|
|
var userRecord User
|
|
|
|
var userRecord User
|
|
|
|
@ -44,28 +77,30 @@ func getToken(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
decoder := schema.NewDecoder()
|
|
|
|
decoder := schema.NewDecoder()
|
|
|
|
err = r.ParseForm()
|
|
|
|
err = r.ParseForm()
|
|
|
|
if err != nil {
|
|
|
|
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)
|
|
|
|
err = decoder.Decode(&userInput, r.PostForm)
|
|
|
|
if err != nil {
|
|
|
|
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)
|
|
|
|
db.First(&userRecord)
|
|
|
|
if db.Error != nil {
|
|
|
|
if db.Error != nil {
|
|
|
|
log.Println("db finding user had error",err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "database lookup error", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "database lookup error")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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{
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
|
|
|
"name":userRecord.Name,
|
|
|
|
"name":userRecord.Name,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
tokenString, err := token.SignedString([]byte("TODO"))
|
|
|
|
tokenString, err := token.SignedString([]byte("TODO"))
|
|
|
|
if err != nil {
|
|
|
|
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)
|
|
|
|
log.Println("authenticated user", userRecord.Name, "with token", tokenString)
|
|
|
|
@ -74,7 +109,6 @@ func getToken(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
log.Println("getToken user not found")
|
|
|
|
|
|
|
|
return ehttp.NewErrorf(http.StatusForbidden, "Could not find user/pass")
|
|
|
|
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()
|
|
|
|
decoder := schema.NewDecoder()
|
|
|
|
err := r.ParseForm()
|
|
|
|
err := r.ParseForm()
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Println("could not parse form because", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not parse form", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not parse form")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var input User
|
|
|
|
var input User
|
|
|
|
err = decoder.Decode(&input, r.PostForm)
|
|
|
|
err = decoder.Decode(&input, r.PostForm)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
log.Println("could not decode user because", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not decode user", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not decode user")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
res, err := govalidator.ValidateStruct(input)
|
|
|
|
res, err := govalidator.ValidateStruct(input)
|
|
|
|
if err != nil || res != true {
|
|
|
|
if err != nil || res != true {
|
|
|
|
log.Println("user",input,"was invalid because", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusBadRequest, "could not validate your data", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusBadRequest, "could not validate your data")
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
ret := db.NewRecord(input)
|
|
|
|
if ret != true {
|
|
|
|
if ret != true {
|
|
|
|
log.Println("cannot register user",input,"cannot create record")
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not create user", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not create user")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
db.Create(&input)
|
|
|
|
db.Create(&input)
|
|
|
|
log.Println("registered user", input)
|
|
|
|
log.Println("registered user", input.Name)
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
func main() {
|
|
|
|
var err error
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
|
|
@ -124,8 +169,15 @@ func main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
db.LogMode(false)
|
|
|
|
db.LogMode(false)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create or update schemas …
|
|
|
|
// Create or update schemas …
|
|
|
|
db.AutoMigrate(&User{})
|
|
|
|
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()
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
|
|
// define URL handlers
|
|
|
|
// define URL handlers
|
|
|
|
@ -136,3 +188,4 @@ func main() {
|
|
|
|
http.Handle("/", r)
|
|
|
|
http.Handle("/", r)
|
|
|
|
http.ListenAndServe(":8000", nil)
|
|
|
|
http.ListenAndServe(":8000", nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|