fyp/users.go

356 lines
8.1 KiB
Go

package main
import (
"crypto/rand"
"database/sql"
"encoding/hex"
"io"
"net/http"
"golang.org/x/crypto/bcrypt"
dbtypes "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
"git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
func generateSalt() string {
salt := make([]byte, 4)
_, err := io.ReadFull(rand.Reader, salt)
if err != nil {
panic("TODO handle this better")
}
return hex.EncodeToString(salt)
}
func hashPassword(password string, salt string) (string, error) {
bytes_salt, err := hex.DecodeString(salt)
if err != nil {
return "", err
}
bytes, err := bcrypt.GenerateFromPassword(append([]byte(password), bytes_salt...), 14)
return string(bytes), err
}
func genToken() string {
token := make([]byte, 60)
_, err := io.ReadFull(rand.Reader, token)
if err != nil {
panic("TODO handle this better")
}
return hex.EncodeToString(token)
}
func generateToken(db *sql.DB, email string, password string) (string, bool) {
row, err := db.Query("select id, salt, password from users where email = $1;", email)
if err != nil || !row.Next() {
return "", false
}
var db_id string
var db_salt string
var db_password string
err = row.Scan(&db_id, &db_salt, &db_password)
if err != nil {
return "", false
}
bytes_salt, err := hex.DecodeString(db_salt)
if err != nil {
panic("TODO handle better! Somethign is wrong with salt being stored in the database")
}
if err = bcrypt.CompareHashAndPassword([]byte(db_password), append([]byte(password), bytes_salt...)); err != nil {
return "", false
}
token := genToken()
_, err = db.Exec("insert into tokens (user_id, token) values ($1, $2);", db_id, token)
if err != nil {
return "", false
}
return token, true
}
func usersEndpints(db *sql.DB, handle *Handle) {
handle.Post("/login", func(c *Context) *Error {
type UserLogin struct {
Email string `json:"email"`
Password string `json:"password"`
}
var dat UserLogin
if err := c.ToJSON(&dat); err != nil {
return err
}
// TODO Give this to the generateToken function
token, login := generateToken(db, dat.Email, dat.Password)
if !login {
return c.SendJSONStatus(http.StatusUnauthorized, "Email or password are incorrect")
}
user, err := dbtypes.UserFromToken(c.Db, token)
if err != nil {
return c.Error500(err)
}
type UserReturn struct {
Token string `json:"token"`
Id string `json:"id"`
UserType int `json:"user_type"`
Username string `json:"username"`
Email string `json:"email"`
}
userReturn := UserReturn{
Token: token,
Id: user.Id,
UserType: user.UserType,
Username: user.Username,
Email: user.Email,
}
return c.SendJSON(userReturn)
})
handle.Post("/register", func(c *Context) *Error {
type UserLogin struct {
Username string `json:"username"`
Email string `json:"email"`
Password string `json:"password"`
}
var dat UserLogin
if err := c.ToJSON(&dat); err != nil {
return err
}
if len(dat.Username) == 0 || len(dat.Password) == 0 || len(dat.Email) == 0 {
return c.SendJSONStatus(http.StatusBadRequest, "Please provide a valid json")
}
rows, err := db.Query("select username, email from users where username=$1 or email=$2;", dat.Username, dat.Email)
if err != nil {
return c.Error500(err)
}
defer rows.Close()
if rows.Next() {
var db_username string
var db_email string
err = rows.Scan(&db_username, &db_email)
if err != nil {
return c.Error500(err)
}
if db_email == dat.Email {
return c.SendJSONStatus(http.StatusBadRequest, "Email already in use!")
}
if db_username == dat.Username {
return c.SendJSONStatus(http.StatusBadRequest, "Username already in use!")
}
panic("Unrechable")
}
if len([]byte(dat.Password)) > 68 {
return c.JsonBadRequest("Password is to long!")
}
salt := generateSalt()
hash_password, err := hashPassword(dat.Password, salt)
if err != nil {
return c.Error500(err)
}
_, err = db.Exec("insert into users (username, email, salt, password) values ($1, $2, $3, $4);", dat.Username, dat.Email, salt, hash_password)
if err != nil {
return c.Error500(err)
}
// TODO Give this to the generateToken function
token, login := generateToken(db, dat.Email, dat.Password)
if !login {
return c.SendJSONStatus(500, "Could not login after creatting account please try again later")
}
user, err := dbtypes.UserFromToken(c.Db, token)
if err != nil {
return c.Error500(err)
}
type UserReturn struct {
Token string `json:"token"`
Id string `json:"id"`
UserType int `json:"user_type"`
Username string `json:"username"`
Email string `json:"email"`
}
userReturn := UserReturn{
Token: token,
Id: user.Id,
UserType: user.UserType,
Username: user.Username,
Email: user.Email,
}
return c.SendJSON(userReturn)
})
// TODO allow admin users to update this data
handle.Get("/user/info", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
user, err := dbtypes.UserFromToken(c.Db, *c.Token)
if err != nil {
return c.Error500(err)
}
type UserReturn struct {
Id string `json:"id"`
UserType int `json:"user_type"`
Username string `json:"username"`
Email string `json:"email"`
}
userReturn := UserReturn{
Id: user.Id,
UserType: user.UserType,
Username: user.Username,
Email: user.Email,
}
return c.SendJSON(userReturn)
})
// Handles updating users
handle.Post("/user/info", func(c *Context) *Error {
if !c.CheckAuthLevel(int(dbtypes.User_Normal)) {
return nil
}
type UserData struct {
Id string `json:"id"`
Email string `json:"email"`
}
var dat UserData
if err := c.ToJSON(&dat); err != nil {
return err
}
if dat.Id != c.User.Id && c.User.UserType != int(dbtypes.User_Admin) {
return c.SendJSONStatus(403, "You need to be an admin to update another users account")
}
if dat.Id != c.User.Id {
var data struct {
Id string
}
err := utils.GetDBOnce(c, &data, "users where id=$1", dat.Id)
if err == NotFoundError {
return c.JsonBadRequest("User does not exist")
} else if err != nil {
return c.Error500(err)
}
}
var data struct {
Id string
}
err := utils.GetDBOnce(c, &data, "users where email=$1", dat.Email)
if err != nil && err != NotFoundError {
return c.Error500(err)
}
if err != NotFoundError {
if data.Id == dat.Id {
return c.JsonBadRequest("Email is the name as the previous one!")
} else {
return c.JsonBadRequest("Email already in use")
}
}
_, err = c.Db.Exec("update users set email=$2 where id=$1", dat.Id, dat.Email)
if err != nil {
return c.Error500(err)
}
var user struct {
Id string
Username string
Email string
User_Type int
}
err = utils.GetDBOnce(c, &user, "users where id=$1", dat.Id)
if err != nil {
return c.Error500(err)
}
toReturnUser := dbtypes.User{
Id: user.Id,
Username: user.Username,
Email: user.Email,
UserType: user.User_Type,
}
return c.SendJSON(toReturnUser)
})
handle.Post("/user/info/password", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
var dat struct {
Old_Password string `json:"old_password"`
Password string `json:"password"`
Password2 string `json:"password2"`
}
if err := c.ToJSON(&dat); err != nil {
return err
}
if dat.Password == "" {
return c.JsonBadRequest("Password can not be empty")
}
if dat.Password != dat.Password2 {
return c.JsonBadRequest("New passwords did not match")
}
c.Logger.Warn("test", "dat", dat)
_, login := generateToken(db, c.User.Email, dat.Old_Password)
if !login {
return c.JsonBadRequest("Password is incorrect")
}
salt := generateSalt()
hash_password, err := hashPassword(dat.Password, salt)
if err != nil {
return c.Error500(err)
}
_, err = db.Exec("update users set salt=$1, password=$2 where id=$3", salt, hash_password, c.User.Id)
if err != nil {
return c.Error500(err)
}
return c.SendJSON(c.User.Id)
})
// TODO create function to remove token
}