mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-12-26 00:29:01 +00:00
Register API complete, email validation too
This commit is contained in:
parent
9420566cc9
commit
1018742a22
@ -36,7 +36,7 @@ func HandleRequests() {
|
||||
v1.HandleFunc("/profile/{id}", jwtMiddleware(serveEndpoint))
|
||||
|
||||
// No Auth
|
||||
v1.HandleFunc("/register", serveEndpoint).Methods("POST")
|
||||
v1.HandleFunc("/register", handleRegister).Methods("POST")
|
||||
v1.HandleFunc("/login", serveEndpoint).Methods("POST")
|
||||
v1.HandleFunc("/logout", serveEndpoint).Methods("POST")
|
||||
|
||||
@ -52,16 +52,30 @@ func HandleRequests() {
|
||||
}
|
||||
|
||||
// MIDDLEWARE
|
||||
// throwUnauthorized - Throws a 403
|
||||
func throwUnauthorized(w http.ResponseWriter, m string) {
|
||||
jr := jsonResponse{
|
||||
Err: m,
|
||||
}
|
||||
js, _ := json.Marshal(&jr)
|
||||
err := errors.New(string(js))
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
// throwUnauthorized - Throws a 403 :
|
||||
func throwBadReq(w http.ResponseWriter, m string) {
|
||||
jr := jsonResponse{
|
||||
Err: m,
|
||||
}
|
||||
js, _ := json.Marshal(&jr)
|
||||
err := errors.New(string(js))
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// tokenMiddleware - Validates token to a user
|
||||
func tokenMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(res http.ResponseWriter, req *http.Request) {
|
||||
jr := jsonResponse{
|
||||
Err: "Invalid API Token",
|
||||
}
|
||||
js, _ := json.Marshal(&jr)
|
||||
err := errors.New(string(js))
|
||||
http.Error(res, err.Error(), http.StatusUnauthorized)
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
throwUnauthorized(w, "Invalid API Token")
|
||||
return
|
||||
// next(res, req)
|
||||
}
|
||||
@ -69,31 +83,45 @@ func tokenMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
|
||||
// jwtMiddleware - Validates middleware to a user
|
||||
func jwtMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(res http.ResponseWriter, req *http.Request) {
|
||||
jr := jsonResponse{
|
||||
Err: "Invalid JWT Token",
|
||||
}
|
||||
js, _ := json.Marshal(&jr)
|
||||
err := errors.New(string(js))
|
||||
http.Error(res, err.Error(), http.StatusUnauthorized)
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
throwUnauthorized(w, "Invalid JWT Token")
|
||||
return
|
||||
// next(res, req)
|
||||
}
|
||||
}
|
||||
|
||||
// ENDPOINT HANDLING
|
||||
// API ENDPOINT HANDLING
|
||||
|
||||
// handleRegister - Does as it says!
|
||||
func handleRegister(w http.ResponseWriter, r *http.Request) {
|
||||
regReq := RegisterRequest{}
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(®Req)
|
||||
if err != nil {
|
||||
throwBadReq(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = createUser(®Req)
|
||||
if err != nil {
|
||||
throwBadReq(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Lets trick 'em for now ;) ;)
|
||||
fmt.Fprintf(w, "{}")
|
||||
}
|
||||
|
||||
// serveEndpoint - API stuffs
|
||||
func serveEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
var jsonInput map[string]interface{}
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&jsonInput)
|
||||
json, err := decodeJson(r.Body)
|
||||
if err != nil {
|
||||
// If we can't decode. Lets tell them nicely.
|
||||
http.Error(w, "{\"error\":\"Invalid JSON\"}", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("%v", json)
|
||||
// Lets trick 'em for now ;) ;)
|
||||
fmt.Fprintf(w, "{}")
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package goscrobble
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
@ -10,44 +11,59 @@ import (
|
||||
const bCryptCost = 16
|
||||
|
||||
type User struct {
|
||||
UUID string `json:"uuid"`
|
||||
UUID string `json:"uuid"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Username string `json:"username"`
|
||||
password []byte
|
||||
Email string `json:"email"`
|
||||
Verified bool `json:"verified"`
|
||||
Active bool `json:"active"`
|
||||
Admin bool `json:"admin"`
|
||||
}
|
||||
|
||||
// RegisterRequest - Incoming JSON
|
||||
type RegisterRequest struct {
|
||||
Username string `json:"username"`
|
||||
password []byte
|
||||
Email string `json:"email"`
|
||||
Verified bool `json:"verified"`
|
||||
Active bool `json:"active"`
|
||||
Admin bool `json:"admin"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// createUser - Called from API
|
||||
func createUser(username string, email string, password string) error {
|
||||
func createUser(req *RegisterRequest) error {
|
||||
// Check if user already exists..
|
||||
if len(password) < 8 {
|
||||
if len(req.Password) < 8 {
|
||||
return errors.New("Password must be at least 8 characters")
|
||||
}
|
||||
|
||||
// Check username is set
|
||||
if username == "" {
|
||||
if req.Username == "" {
|
||||
return errors.New("A username is required")
|
||||
}
|
||||
|
||||
// If set an email.. validate it!
|
||||
if req.Email != "" {
|
||||
if !isEmailValid(req.Email) {
|
||||
return errors.New("Invalid email address")
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user or email exists!
|
||||
if userAlreadyExists(username, email) {
|
||||
if userAlreadyExists(req) {
|
||||
return errors.New("Username or email already exists")
|
||||
}
|
||||
|
||||
// Lets hashit!
|
||||
hash, err := hashPassword(password)
|
||||
hash, err := hashPassword(req.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return insertUser(username, email, hash)
|
||||
return insertUser(req.Username, req.Email, hash)
|
||||
}
|
||||
|
||||
// insertUser - Does the dirtywork!
|
||||
func insertUser(username string, email string, password []byte) error {
|
||||
_, err := db.Exec("INSERT INTO users (uuid, username, email, password) VALUES (UUID_TO_BIN(UUID(), true),'?','?','?')", username, email, password)
|
||||
_, err := db.Exec("INSERT INTO users (uuid, created_at, username, email, password) VALUES (UUID_TO_BIN(UUID(), true),NOW(),?,?,?)", username, email, password)
|
||||
|
||||
return err
|
||||
}
|
||||
@ -69,14 +85,16 @@ func isValidPassword(password string, user User) bool {
|
||||
|
||||
// userAlreadyExists - Returns bool indicating if a record exists for either username or email
|
||||
// Using two look ups to make use of DB indexes.
|
||||
func userAlreadyExists(username string, email string) bool {
|
||||
var usernameCount, emailCount int
|
||||
err := db.QueryRow("SELECT COUNT(*) FROM users WHERE username = '?'", username).Scan(&usernameCount)
|
||||
// Only run email check if there's an email...
|
||||
if email != "" {
|
||||
err = db.QueryRow("SELECT COUNT(*) FROM users WHERE email = '?'", email).Scan(&emailCount)
|
||||
} else {
|
||||
emailCount = 0
|
||||
func userAlreadyExists(req *RegisterRequest) bool {
|
||||
var userExists int
|
||||
err := db.QueryRow("SELECT COUNT(*) FROM users WHERE username = ?", req.Username).Scan(&userExists)
|
||||
if userExists > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
if req.Email != "" {
|
||||
// Only run email check if there's an email...
|
||||
err = db.QueryRow("SELECT COUNT(*) FROM users WHERE email = ?", req.Email).Scan(&userExists)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -84,8 +102,5 @@ func userAlreadyExists(username string, email string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
count := usernameCount + emailCount
|
||||
|
||||
// If there is more than one.. Return true. User exists.
|
||||
return count != 0
|
||||
return userExists > 0
|
||||
}
|
||||
|
@ -1 +1,26 @@
|
||||
package goscrobble
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
||||
|
||||
// decodeJson - Returns a map[string]interface{}
|
||||
func decodeJson(body io.ReadCloser) (map[string]interface{}, error) {
|
||||
var jsonInput map[string]interface{}
|
||||
decoder := json.NewDecoder(body)
|
||||
err := decoder.Decode(&jsonInput)
|
||||
|
||||
return jsonInput, err
|
||||
}
|
||||
|
||||
// isEmailValid - checks if the email provided passes the required structure and length.
|
||||
func isEmailValid(e string) bool {
|
||||
if len(e) < 3 && len(e) > 254 {
|
||||
return false
|
||||
}
|
||||
return emailRegex.MatchString(e)
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
DROP TABLE IF EXISTS users;
|
||||
DROP TABLE IF EXISTS `users`;
|
@ -1,5 +1,6 @@
|
||||
CREATE TABLE IF NOT EXISTS `users` (
|
||||
`uuid` BINARY(16) PRIMARY KEY,
|
||||
`created_at` DATETIME NOT NULL,
|
||||
`username` VARCHAR(64) NOT NULL,
|
||||
`password` VARCHAR(60) NOT NULL,
|
||||
`email` VARCHAR(255) NULL DEFAULT NULL,
|
||||
|
Loading…
Reference in New Issue
Block a user