|
|
|
@ -7,9 +7,11 @@ import (
|
|
|
|
"crypto/rand"
|
|
|
|
"crypto/rand"
|
|
|
|
"reflect"
|
|
|
|
"reflect"
|
|
|
|
"log"
|
|
|
|
"log"
|
|
|
|
|
|
|
|
"os"
|
|
|
|
|
|
|
|
|
|
|
|
// muxer and form parser
|
|
|
|
// muxer and form parser
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
|
|
|
|
"github.com/gorilla/handlers"
|
|
|
|
"github.com/gorilla/schema"
|
|
|
|
"github.com/gorilla/schema"
|
|
|
|
|
|
|
|
|
|
|
|
// error returns for HandlerFunc
|
|
|
|
// error returns for HandlerFunc
|
|
|
|
@ -72,28 +74,33 @@ func getToken(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
var userInput User
|
|
|
|
var userInput User
|
|
|
|
var userRecord User
|
|
|
|
var userRecord User
|
|
|
|
var err error
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
|
|
decoder := schema.NewDecoder()
|
|
|
|
decoder := schema.NewDecoder()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Parse form data
|
|
|
|
err = r.ParseForm()
|
|
|
|
err = r.ParseForm()
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not parse form", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not parse form", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err = decoder.Decode(&userInput, r.PostForm)
|
|
|
|
// Decode to struct
|
|
|
|
|
|
|
|
err = decoder.Decode(&userInput, r.Form)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not decode user from form", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not decode user from form", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Find user in DB
|
|
|
|
db.First(&userRecord)
|
|
|
|
db.First(&userRecord)
|
|
|
|
if db.Error != nil {
|
|
|
|
if db.Error != nil {
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "database lookup error", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "database lookup error", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check hashed PW
|
|
|
|
hash, err := scrypt.Key([]byte(userInput.Pass), []byte(userRecord.Salt), 16384, 8, 1, 32)
|
|
|
|
hash, err := scrypt.Key([]byte(userInput.Pass), []byte(userRecord.Salt), 16384, 8, 1, 32)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "cannot hash pass", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "cannot hash pass", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if userRecord.Name == userInput.Name && reflect.DeepEqual(hash, []byte(userRecord.Pass)) {
|
|
|
|
if userRecord.Name == userInput.Name && reflect.DeepEqual(hash, []byte(userRecord.Pass)) {
|
|
|
|
|
|
|
|
// Generate JWT-token
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
|
|
|
"name":userRecord.Name,
|
|
|
|
"name":userRecord.Name,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
@ -102,41 +109,47 @@ func getToken(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
return ehttp.NewErrorf(http.StatusForbidden, "could not construct token", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusForbidden, "could not construct token", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
log.Println("authenticated user", userRecord.Name, "with token", tokenString)
|
|
|
|
// Reply with token
|
|
|
|
jsonOut, _ := json.Marshal(map[string]string{"token": tokenString})
|
|
|
|
jsonOut, _ := json.Marshal(map[string]string{"token": tokenString})
|
|
|
|
fmt.Fprint(w, string(jsonOut))
|
|
|
|
fmt.Fprint(w, string(jsonOut))
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// User was not found
|
|
|
|
return ehttp.NewErrorf(http.StatusForbidden, "Could not find user/pass")
|
|
|
|
return ehttp.NewErrorf(http.StatusForbidden, "Could not find user/pass")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func register(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
func register(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
decoder := schema.NewDecoder()
|
|
|
|
decoder := schema.NewDecoder()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Parse http request to request-struct
|
|
|
|
err := r.ParseForm()
|
|
|
|
err := r.ParseForm()
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not parse form", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not parse form", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Decode request into struct
|
|
|
|
var input User
|
|
|
|
var input User
|
|
|
|
err = decoder.Decode(&input, r.PostForm)
|
|
|
|
err = decoder.Decode(&input, r.PostForm)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not decode user", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not decode user", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Validate struct
|
|
|
|
res, err := govalidator.ValidateStruct(input)
|
|
|
|
res, err := govalidator.ValidateStruct(input)
|
|
|
|
if err != nil || res != true {
|
|
|
|
if err != nil || res != true {
|
|
|
|
return ehttp.NewErrorf(http.StatusBadRequest, "could not validate your data", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusBadRequest, "could not validate your data", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if users already exists
|
|
|
|
countName := 0; countEmail := 0
|
|
|
|
countName := 0; countEmail := 0
|
|
|
|
db.Model(&User{}).Where("name = ?", input.Name).Count(&countName)
|
|
|
|
db.Model(&User{}).Where("name = ?", input.Name).Count(&countName)
|
|
|
|
db.Model(&User{}).Where("email = ?", input.Email).Count(&countEmail)
|
|
|
|
db.Model(&User{}).Where("email = ?", input.Email).Count(&countEmail)
|
|
|
|
log.Println("have countName", countName, "mail", countEmail)
|
|
|
|
|
|
|
|
if countName != 0 || countEmail != 0{
|
|
|
|
if countName != 0 || countEmail != 0{
|
|
|
|
return ehttp.NewErrorf(http.StatusConflict, "username or email already exists")
|
|
|
|
return ehttp.NewErrorf(http.StatusConflict, "username or email already exists")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Generate salt
|
|
|
|
salt := make([]byte, 64)
|
|
|
|
salt := make([]byte, 64)
|
|
|
|
_, err = rand.Read(salt)
|
|
|
|
_, err = rand.Read(salt)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
@ -144,11 +157,14 @@ func register(w http.ResponseWriter, r *http.Request) error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
input.Salt = string(salt)
|
|
|
|
input.Salt = string(salt)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Generate scrypt hash of pass
|
|
|
|
hash, err := scrypt.Key([]byte(input.Pass), salt, 16384, 8, 1, 32)
|
|
|
|
hash, err := scrypt.Key([]byte(input.Pass), salt, 16384, 8, 1, 32)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not hash pass", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not hash pass", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
input.Pass = string(hash)
|
|
|
|
input.Pass = string(hash)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// add to database
|
|
|
|
ret := db.NewRecord(input)
|
|
|
|
ret := db.NewRecord(input)
|
|
|
|
if ret != true {
|
|
|
|
if ret != true {
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not create user", err)
|
|
|
|
return ehttp.NewErrorf(http.StatusInternalServerError, "could not create user", err)
|
|
|
|
@ -182,10 +198,33 @@ func main() {
|
|
|
|
|
|
|
|
|
|
|
|
// define URL handlers
|
|
|
|
// define URL handlers
|
|
|
|
r := mux.NewRouter()
|
|
|
|
r := mux.NewRouter()
|
|
|
|
r.Handle("/getToken", ehttp.HandlerFunc(getToken))
|
|
|
|
|
|
|
|
r.Handle("/register", ehttp.HandlerFunc(register))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
http.Handle("/", r)
|
|
|
|
// User Management
|
|
|
|
|
|
|
|
r.Handle("/users", ehttp.HandlerFunc(register)).Methods("POST") // Make new user
|
|
|
|
|
|
|
|
r.Handle("/users", ehttp.HandlerFunc(getToken)).Methods("GET") // Get user „info“(=token)
|
|
|
|
|
|
|
|
// TODO: delete, modify
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// List Management
|
|
|
|
|
|
|
|
r.Handle("/lists", ehttp.HandlerFunc(nil)).Methods("POST") // Make a new list
|
|
|
|
|
|
|
|
r.Handle("/lists", ehttp.HandlerFunc(nil)).Methods("GET") // Get list of lists
|
|
|
|
|
|
|
|
r.Handle("/lists/{name}", ehttp.HandlerFunc(nil)).Methods("PUT") // Update list (name, icon, …)
|
|
|
|
|
|
|
|
r.Handle("/lists/{name}", ehttp.HandlerFunc(nil)).Methods("DELETE") // Delete a list
|
|
|
|
|
|
|
|
r.Handle("/lists/{name}", ehttp.HandlerFunc(nil)).Methods("GET") // Get list content (i.e., items)
|
|
|
|
|
|
|
|
// List sharing
|
|
|
|
|
|
|
|
r.Handle("/lists/{name}/sharers", ehttp.HandlerFunc(nil)).Methods("POST") // Add new sharer
|
|
|
|
|
|
|
|
r.Handle("/lists/{name}/sharers", ehttp.HandlerFunc(nil)).Methods("GET") // Get list of sharers
|
|
|
|
|
|
|
|
r.Handle("/lists/{name}/sharers/{user}", ehttp.HandlerFunc(nil)).Methods("DELETE") // Remove sharer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
r.Handle("/lists/{name}/items", ehttp.HandlerFunc(nil)).Methods("PUT") // Add new item
|
|
|
|
|
|
|
|
r.Handle("/lists/{name}/items/{item}", ehttp.HandlerFunc(nil)).Methods("DELETE") // Delete item
|
|
|
|
|
|
|
|
r.Handle("/lists/{name}/items/{item}", ehttp.HandlerFunc(nil)).Methods("POST") // Update item
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Unit-history (unit is a ever-growing list only used for auto-completion)
|
|
|
|
|
|
|
|
r.Handle("/units", ehttp.HandlerFunc(nil)).Methods("POST") // Make a new unit
|
|
|
|
|
|
|
|
r.Handle("/units", ehttp.HandlerFunc(nil)).Methods("GET") // Get list of units
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
loggedRouter := handlers.LoggingHandler(os.Stdout, r)
|
|
|
|
|
|
|
|
http.Handle("/", loggedRouter)
|
|
|
|
http.ListenAndServe(":8000", nil)
|
|
|
|
http.ListenAndServe(":8000", nil)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|