mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-11-25 01:45:15 +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))
|
v1.HandleFunc("/profile/{id}", jwtMiddleware(serveEndpoint))
|
||||||
|
|
||||||
// No Auth
|
// No Auth
|
||||||
v1.HandleFunc("/register", serveEndpoint).Methods("POST")
|
v1.HandleFunc("/register", handleRegister).Methods("POST")
|
||||||
v1.HandleFunc("/login", serveEndpoint).Methods("POST")
|
v1.HandleFunc("/login", serveEndpoint).Methods("POST")
|
||||||
v1.HandleFunc("/logout", serveEndpoint).Methods("POST")
|
v1.HandleFunc("/logout", serveEndpoint).Methods("POST")
|
||||||
|
|
||||||
@ -52,16 +52,30 @@ func HandleRequests() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MIDDLEWARE
|
// 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
|
// tokenMiddleware - Validates token to a user
|
||||||
func tokenMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
func tokenMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||||
return func(res http.ResponseWriter, req *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
jr := jsonResponse{
|
throwUnauthorized(w, "Invalid API Token")
|
||||||
Err: "Invalid API Token",
|
|
||||||
}
|
|
||||||
js, _ := json.Marshal(&jr)
|
|
||||||
err := errors.New(string(js))
|
|
||||||
http.Error(res, err.Error(), http.StatusUnauthorized)
|
|
||||||
return
|
return
|
||||||
// next(res, req)
|
// next(res, req)
|
||||||
}
|
}
|
||||||
@ -69,31 +83,45 @@ func tokenMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
|
|
||||||
// jwtMiddleware - Validates middleware to a user
|
// jwtMiddleware - Validates middleware to a user
|
||||||
func jwtMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
func jwtMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||||
return func(res http.ResponseWriter, req *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
jr := jsonResponse{
|
throwUnauthorized(w, "Invalid JWT Token")
|
||||||
Err: "Invalid JWT Token",
|
|
||||||
}
|
|
||||||
js, _ := json.Marshal(&jr)
|
|
||||||
err := errors.New(string(js))
|
|
||||||
http.Error(res, err.Error(), http.StatusUnauthorized)
|
|
||||||
return
|
return
|
||||||
// next(res, req)
|
// 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
|
// serveEndpoint - API stuffs
|
||||||
func serveEndpoint(w http.ResponseWriter, r *http.Request) {
|
func serveEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||||
var jsonInput map[string]interface{}
|
json, err := decodeJson(r.Body)
|
||||||
decoder := json.NewDecoder(r.Body)
|
|
||||||
err := decoder.Decode(&jsonInput)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If we can't decode. Lets tell them nicely.
|
// If we can't decode. Lets tell them nicely.
|
||||||
http.Error(w, "{\"error\":\"Invalid JSON\"}", http.StatusBadRequest)
|
http.Error(w, "{\"error\":\"Invalid JSON\"}", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("%v", json)
|
||||||
// Lets trick 'em for now ;) ;)
|
// Lets trick 'em for now ;) ;)
|
||||||
fmt.Fprintf(w, "{}")
|
fmt.Fprintf(w, "{}")
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package goscrobble
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
@ -10,44 +11,59 @@ import (
|
|||||||
const bCryptCost = 16
|
const bCryptCost = 16
|
||||||
|
|
||||||
type User struct {
|
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"`
|
Username string `json:"username"`
|
||||||
password []byte
|
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Verified bool `json:"verified"`
|
Password string `json:"password"`
|
||||||
Active bool `json:"active"`
|
|
||||||
Admin bool `json:"admin"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// createUser - Called from API
|
// createUser - Called from API
|
||||||
func createUser(username string, email string, password string) error {
|
func createUser(req *RegisterRequest) error {
|
||||||
// Check if user already exists..
|
// Check if user already exists..
|
||||||
if len(password) < 8 {
|
if len(req.Password) < 8 {
|
||||||
return errors.New("Password must be at least 8 characters")
|
return errors.New("Password must be at least 8 characters")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check username is set
|
// Check username is set
|
||||||
if username == "" {
|
if req.Username == "" {
|
||||||
return errors.New("A username is required")
|
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!
|
// Check if user or email exists!
|
||||||
if userAlreadyExists(username, email) {
|
if userAlreadyExists(req) {
|
||||||
return errors.New("Username or email already exists")
|
return errors.New("Username or email already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lets hashit!
|
// Lets hashit!
|
||||||
hash, err := hashPassword(password)
|
hash, err := hashPassword(req.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return insertUser(username, email, hash)
|
return insertUser(req.Username, req.Email, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertUser - Does the dirtywork!
|
// insertUser - Does the dirtywork!
|
||||||
func insertUser(username string, email string, password []byte) error {
|
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
|
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
|
// userAlreadyExists - Returns bool indicating if a record exists for either username or email
|
||||||
// Using two look ups to make use of DB indexes.
|
// Using two look ups to make use of DB indexes.
|
||||||
func userAlreadyExists(username string, email string) bool {
|
func userAlreadyExists(req *RegisterRequest) bool {
|
||||||
var usernameCount, emailCount int
|
var userExists int
|
||||||
err := db.QueryRow("SELECT COUNT(*) FROM users WHERE username = '?'", username).Scan(&usernameCount)
|
err := db.QueryRow("SELECT COUNT(*) FROM users WHERE username = ?", req.Username).Scan(&userExists)
|
||||||
// Only run email check if there's an email...
|
if userExists > 0 {
|
||||||
if email != "" {
|
return true
|
||||||
err = db.QueryRow("SELECT COUNT(*) FROM users WHERE email = '?'", email).Scan(&emailCount)
|
}
|
||||||
} else {
|
|
||||||
emailCount = 0
|
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 {
|
if err != nil {
|
||||||
@ -84,8 +102,5 @@ func userAlreadyExists(username string, email string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
count := usernameCount + emailCount
|
return userExists > 0
|
||||||
|
|
||||||
// If there is more than one.. Return true. User exists.
|
|
||||||
return count != 0
|
|
||||||
}
|
}
|
||||||
|
@ -1 +1,26 @@
|
|||||||
package goscrobble
|
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` (
|
CREATE TABLE IF NOT EXISTS `users` (
|
||||||
`uuid` BINARY(16) PRIMARY KEY,
|
`uuid` BINARY(16) PRIMARY KEY,
|
||||||
|
`created_at` DATETIME NOT NULL,
|
||||||
`username` VARCHAR(64) NOT NULL,
|
`username` VARCHAR(64) NOT NULL,
|
||||||
`password` VARCHAR(60) NOT NULL,
|
`password` VARCHAR(60) NOT NULL,
|
||||||
`email` VARCHAR(255) NULL DEFAULT NULL,
|
`email` VARCHAR(255) NULL DEFAULT NULL,
|
||||||
|
Loading…
Reference in New Issue
Block a user