Compare commits
2 Commits
57acac6dae
...
201d6c6e51
| Author | SHA1 | Date | |
|---|---|---|---|
| 201d6c6e51 | |||
| b6b92d38be |
15
config/config.go
Normal file
15
config/config.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package config
|
||||
|
||||
const MIN_USERNAME_LENGTH = 4
|
||||
|
||||
const MAX_USERNAME_LENGTH = 20
|
||||
|
||||
const MIN_PASSWORD_LENGTH = 8
|
||||
|
||||
const MAX_PASSWORD_LENGTH = 128
|
||||
|
||||
const PASSWORD_REGEX = `^[A-Za-z0-9!@#$%^&*(),.?":{}|<>]+$`
|
||||
|
||||
const USERNAME_REGEX = `^[a-zA-Z0-9_]+$`
|
||||
|
||||
const BCRYPT_COST = 16
|
||||
@@ -73,3 +73,34 @@ func InsertUser(db *sql.DB, username string, passwordHash string) (string, error
|
||||
}
|
||||
return userId, err
|
||||
}
|
||||
|
||||
func GetPasswordHash(db *sql.DB, username string) (string, error) {
|
||||
query := `
|
||||
SELECT password_hash FROM Accounts
|
||||
WHERE LOWER(username) = LOWER($1);
|
||||
`
|
||||
|
||||
var passwordHash string
|
||||
err := db.QueryRow(query, username).Scan(&passwordHash)
|
||||
if err != nil {
|
||||
fmt.Printf("error getting password: %v\n", err)
|
||||
return "", fmt.Errorf("error getting password: %v", err)
|
||||
}
|
||||
|
||||
return passwordHash, err
|
||||
}
|
||||
|
||||
func GetUserId(db *sql.DB, username string) (string, error) {
|
||||
query := `
|
||||
SELECT user_id FROM Accounts
|
||||
WHERE LOWER(username) = $1;
|
||||
`
|
||||
|
||||
var dbUsername string
|
||||
err := db.QueryRow(query, username).Scan(&dbUsername)
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting password: %v\n", err)
|
||||
return "", fmt.Errorf("error getting user id: %v", err)
|
||||
}
|
||||
return dbUsername, err
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"log"
|
||||
"os"
|
||||
"relay-server/config"
|
||||
"relay-server/database"
|
||||
"relay-server/helpers"
|
||||
"relay-server/models"
|
||||
@@ -25,12 +26,10 @@ func Signup(c *fiber.Ctx) error {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "password is empty"})
|
||||
}
|
||||
|
||||
// Checks if passwords have valid length and characters
|
||||
// Checks if passwords or username have valid length and characters
|
||||
if !helpers.IsValidPassword(u.Password) {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid password"})
|
||||
}
|
||||
// Checks if username have valid length and characters
|
||||
if !helpers.IsValidUsername(u.Username) {
|
||||
} else if !helpers.IsValidUsername(u.Username) {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid username"})
|
||||
}
|
||||
|
||||
@@ -41,9 +40,9 @@ func Signup(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
// Create password hash
|
||||
passwordHash, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
|
||||
passwordHash, err := bcrypt.GenerateFromPassword([]byte(u.Password), config.BCRYPT_COST)
|
||||
if err != nil {
|
||||
log.Printf("error hashing password: %v", err)
|
||||
fmt.Printf("error hashing password: %v", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "internal server error"})
|
||||
}
|
||||
|
||||
@@ -70,5 +69,60 @@ func Signup(c *fiber.Ctx) error {
|
||||
|
||||
// If everything went well sent username and user_id assigned by database
|
||||
return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Successfully signed up", "username": u.Username, "user_id": userId})
|
||||
|
||||
}
|
||||
|
||||
func Login(c *fiber.Ctx) error {
|
||||
db := database.DB
|
||||
u := new(models.LoginStruct)
|
||||
|
||||
if err := c.BodyParser(u); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Checks if username or passwords are empty
|
||||
if u.Username == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "username is empty"})
|
||||
} else if u.Password == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "password is empty"})
|
||||
}
|
||||
|
||||
// Checks if username or passwords have valid length and characters
|
||||
if !helpers.IsValidUsername(u.Username) {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid username"})
|
||||
} else if !helpers.IsValidPassword(u.Password) {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid password"})
|
||||
}
|
||||
|
||||
// Checks if username exist in database
|
||||
exist, _ := database.CheckUserExists(db, u.Username)
|
||||
if !exist {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "user does not exists"})
|
||||
}
|
||||
|
||||
// Verifies password matching
|
||||
passwordHash, _ := database.GetPasswordHash(db, u.Username)
|
||||
if bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(u.Password)) != nil {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "invalid password"})
|
||||
}
|
||||
|
||||
userId, err := database.GetUserId(db, u.Username)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "internal server error"})
|
||||
}
|
||||
// Generate token with user id and username
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"user_id": userId,
|
||||
"username": u.Username,
|
||||
})
|
||||
// Sign token
|
||||
signedToken, err := token.SignedString([]byte(os.Getenv("JWT_SECRET")))
|
||||
|
||||
// Set token to cookies
|
||||
cookie := new(fiber.Cookie)
|
||||
cookie.Name = "token"
|
||||
cookie.Value = signedToken
|
||||
cookie.Expires = time.Now().Add(30 * 24 * time.Hour)
|
||||
cookie.HTTPOnly = true
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Successfully logged in", "username": u.Username, "user_id": userId})
|
||||
}
|
||||
@@ -2,20 +2,9 @@ package helpers
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"relay-server/config"
|
||||
)
|
||||
|
||||
const MIN_USERNAME_LENGTH = 4
|
||||
|
||||
const MAX_USERNAME_LENGTH = 20
|
||||
|
||||
const MIN_PASSWORD_LENGTH = 8
|
||||
|
||||
const MAX_PASSWORD_LENGTH = 128
|
||||
|
||||
const PASSWORD_REGEX = `^[A-Za-z0-9!@#$%^&*(),.?":{}|<>]+$`
|
||||
|
||||
const USERNAME_REGEX = `^[a-zA-Z0-9_]+$`
|
||||
|
||||
func IsValidUsername(username interface{}) bool {
|
||||
// Checks if username is type of string
|
||||
strUsername, ok := username.(string)
|
||||
@@ -23,15 +12,15 @@ func IsValidUsername(username interface{}) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
match, _ := regexp.MatchString(USERNAME_REGEX, strUsername)
|
||||
match, _ := regexp.MatchString(config.USERNAME_REGEX, strUsername)
|
||||
if !match {
|
||||
return false
|
||||
}
|
||||
// Checks if username length is valid
|
||||
if len(strUsername) < MIN_USERNAME_LENGTH {
|
||||
if len(strUsername) < config.MIN_USERNAME_LENGTH {
|
||||
return false
|
||||
}
|
||||
if len(strUsername) > MAX_USERNAME_LENGTH {
|
||||
if len(strUsername) > config.MAX_USERNAME_LENGTH {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -44,15 +33,15 @@ func IsValidPassword(password interface{}) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(strPassword) < MIN_PASSWORD_LENGTH {
|
||||
if len(strPassword) < config.MIN_PASSWORD_LENGTH {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(strPassword) > MAX_PASSWORD_LENGTH {
|
||||
if len(strPassword) > config.MAX_PASSWORD_LENGTH {
|
||||
return false
|
||||
}
|
||||
|
||||
match, _ := regexp.MatchString(PASSWORD_REGEX, strPassword)
|
||||
match, _ := regexp.MatchString(config.PASSWORD_REGEX, strPassword)
|
||||
if !match {
|
||||
return false
|
||||
}
|
||||
|
||||
11
main.go
11
main.go
@@ -20,17 +20,18 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}(db)
|
||||
|
||||
app.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.SendString("Hello, World!")
|
||||
})
|
||||
|
||||
app.Get("/users", func(c *fiber.Ctx) error {
|
||||
users, _ := database.GetUsers(db)
|
||||
return c.JSON(fiber.Map{"users": users})
|
||||
})
|
||||
//app.Get("/users", func(c *fiber.Ctx) error {
|
||||
// users, _ := database.GetUsers(db)
|
||||
// return c.JSON(fiber.Map{"users": users})
|
||||
//})
|
||||
|
||||
app.Post("/api/auth/signup", handlers.Signup)
|
||||
|
||||
app.Post("/api/auth/login", handlers.Login)
|
||||
err = app.Listen(":3000")
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package models
|
||||
|
||||
type LoginStruct struct {
|
||||
Username string `json:"username" xml:"username" form:"username"`
|
||||
Password string `json:"password" xml:"password" form:"password"`
|
||||
}
|
||||
|
||||
type SignupStruct struct {
|
||||
Username string `json:"username" xml:"username" form:"username"`
|
||||
Password string `json:"password" xml:"password" form:"password"`
|
||||
Reference in New Issue
Block a user