From de3c6821941400c17482bafaa6e7b92cdbb98581 Mon Sep 17 00:00:00 2001 From: Nilo Roberto da Cruz Paim Date: Fri, 14 May 2021 15:56:46 -0300 Subject: [PATCH] Inital version. Authentication ready. --- .env | 13 +++ controllers/authController.go | 162 ++++++++++++++++++++++++++++++++++ database/database.go | 53 +++++++++++ go.mod | 17 ++++ go.sum | 51 +++++++++++ main.go | 31 +++++++ models/users.go | 12 +++ routes/routes.go | 19 ++++ utils/utils.go | 28 ++++++ 9 files changed, 386 insertions(+) create mode 100644 .env create mode 100644 controllers/authController.go create mode 100644 database/database.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 models/users.go create mode 100644 routes/routes.go create mode 100644 utils/utils.go diff --git a/.env b/.env new file mode 100644 index 0000000..e7149ff --- /dev/null +++ b/.env @@ -0,0 +1,13 @@ +# Mysql Live + +#Used when creating a JWT. It can be anything. +API_SECRET=nyEX8BZ44KqTXeV2 +API_PORT=8111 + +# DB configuration +DB_HOST=185.167.97.70 +DB_DRIVER=mysql +DB_USER=root +DB_PASSWORD=@407S732nwPhon +DB_NAME=pcast +DB_PORT=3306 \ No newline at end of file diff --git a/controllers/authController.go b/controllers/authController.go new file mode 100644 index 0000000..44db08f --- /dev/null +++ b/controllers/authController.go @@ -0,0 +1,162 @@ +package controllers + +import ( + "api/database" + "api/models" + "api/utils" + "os" + "strconv" + "time" + + "github.com/dgrijalva/jwt-go" + "github.com/gofiber/fiber/v2" + "golang.org/x/crypto/bcrypt" +) + +func Hello(c *fiber.Ctx) error { + return c.SendString("Hello!") +} + +func Login(c *fiber.Ctx) error { + var data map[string]string + + if err := c.BodyParser(&data); err != nil { + return err + } + + var user models.User + + database.DB.Where("email = ?", data["email"]).First(&user) + + if user.Id == 0 { + c.Status(fiber.StatusNotFound) + return c.JSON(fiber.Map{ + "message": "User not found", + }) + } + + if err := bcrypt.CompareHashAndPassword(user.Password, []byte(data["password"])); err != nil { + c.Status(fiber.StatusBadRequest) + return c.JSON(fiber.Map{ + "message": "Incorrect password", + }) + } + + claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{ + Issuer: strconv.Itoa(int(user.Id)), + ExpiresAt: time.Now().Add(time.Hour * 1).Unix(), + }) + + token, err := claims.SignedString([]byte(os.Getenv("API_SECRET"))) + if err != nil { + c.Status(fiber.StatusInternalServerError) + return c.JSON(fiber.Map{ + "message": "Could't login", + }) + } + + cookie := fiber.Cookie{ + Name: "jwt", + Value: token, + Expires: time.Now().Add(time.Hour * 1), + HTTPOnly: true, + } + + c.Cookie(&cookie) + + return c.JSON(token) +} + +func Logout(c *fiber.Ctx) error { + cookie := fiber.Cookie{ + Name: "jwt", + Value: "", + Expires: time.Now().Add(-time.Hour), + HTTPOnly: true, + } + + c.Cookie(&cookie) + + return c.JSON(fiber.Map{ + "message": "Successful logout", + }) +} + +func AddUser(c *fiber.Ctx) error { + var data map[string]string + + if err := c.BodyParser(&data); err != nil { + return err + } + + passwd, _ := utils.HashPassword(data["password"]) + + user := models.User{ + Name: data["name"], + Email: data["email"], + Password: passwd, + UserType: data["usertype"], + Blocked: "N", + First: "S", + Cancelled: "N", + } + + database.DB.Create(&user) + + if user.Id == 0 { + return c.SendStatus(fiber.StatusFound) + } + + c.SendStatus(fiber.StatusCreated) + return c.JSON(user) +} + +func User(c *fiber.Ctx) error { + cookie := c.Cookies("jwt") + + var user models.User + + claims, err := utils.VerifyAuthentication(c, cookie) + if err != nil { + c.Status(fiber.StatusUnauthorized) + return c.JSON(fiber.Map{ + "message": "Unauthenticated", + }) + } + + database.DB.Where("id = ?", claims.Issuer).First(&user) + + if user.Id == 0 { + c.Status(fiber.StatusUnauthorized) + return c.JSON(fiber.Map{ + "message": "Unauthenticated", + }) + } + + return c.JSON(user) +} + +func Users(c *fiber.Ctx) error { + cookie := c.Cookies("jwt") + + var user []models.User + + _, err := utils.VerifyAuthentication(c, cookie) + if err != nil { + c.Status(fiber.StatusUnauthorized) + return c.JSON(fiber.Map{ + "message": "Unauthenticated", + }) + } + + database.DB.Find(&user) + + if len(user) == 0 { + c.Status(fiber.StatusUnauthorized) + return c.JSON(fiber.Map{ + "message": "Unauthenticated", + }) + } + + return c.JSON(user) +} diff --git a/database/database.go b/database/database.go new file mode 100644 index 0000000..4daa3a9 --- /dev/null +++ b/database/database.go @@ -0,0 +1,53 @@ +package database + +import ( + "api/models" + "fmt" + "log" + "os" + + "github.com/joho/godotenv" + + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +// Our database +var DB *gorm.DB + +// NewDatabase - returns a pointer to a new database connection +func ConnectDB() error { + var erre error + + log.Println("Getting environment values") + erre = godotenv.Load() + if erre != nil { + log.Printf("Error getting env, not comming through %v\n", erre) + return erre + } + + DBURL := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local", os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_NAME")) + + log.Println("Opening connection to database") + + db, err := gorm.Open(mysql.Open(DBURL), &gorm.Config{ + Logger: logger.Default.LogMode(logger.Silent), + }) + + if err != nil { + return err + } + + if result := db.AutoMigrate(&models.User{}); result != nil { + return err + } + + // if result := db.AutoMigrate(&models.Event{}); result != nil { + // return result + // } + + DB = db + + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..694a778 --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module api + +go 1.16 + +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/gofiber/fiber/v2 v2.9.0 + github.com/joho/godotenv v1.3.0 + github.com/klauspost/compress v1.12.2 // indirect + github.com/valyala/fasthttp v1.24.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect + golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 // indirect + gorm.io/driver/mysql v1.0.6 + gorm.io/gorm v1.21.9 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f02922a --- /dev/null +++ b/go.sum @@ -0,0 +1,51 @@ +github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E= +github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/gofiber/fiber/v2 v2.9.0 h1:sZsTKlbyGGZ0UdTUn3ItQv5J9FTQUc4J3OS+03lE5m0= +github.com/gofiber/fiber/v2 v2.9.0/go.mod h1:Ah3IJikrKNRepl/HuVawppS25X7FWohwfCSRn7kJG28= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= +github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/klauspost/compress v1.11.8/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.12.2 h1:2KCfW3I9M7nSc5wOqXAlW2v2U6v+w6cbjvbfp+OykW8= +github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.23.0/go.mod h1:0mw2RjXGOzxf4NL2jni3gUQ7LfjjUSiG5sskOUUSEpU= +github.com/valyala/fasthttp v1.24.0 h1:AAiG4oLDUArTb7rYf9oO2bkGooOqCaUF6a2u8asBP3I= +github.com/valyala/fasthttp v1.24.0/go.mod h1:0mw2RjXGOzxf4NL2jni3gUQ7LfjjUSiG5sskOUUSEpU= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20210226101413-39120d07d75e/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015 h1:hZR0X1kPW+nwyJ9xRxqZk1vx5RUObAPBdKVvXPDUH/E= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gorm.io/driver/mysql v1.0.6 h1:mA0XRPjIKi4bkE9nv+NKs6qj6QWOchqUSdWOcpd3x1E= +gorm.io/driver/mysql v1.0.6/go.mod h1:KdrTanmfLPPyAOeYGyG+UpDys7/7eeWT1zCq+oekYnU= +gorm.io/gorm v1.21.9 h1:INieZtn4P2Pw6xPJ8MzT0G4WUOsHq3RhfuDF1M6GW0E= +gorm.io/gorm v1.21.9/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= diff --git a/main.go b/main.go new file mode 100644 index 0000000..93b2ed2 --- /dev/null +++ b/main.go @@ -0,0 +1,31 @@ +package main + +import ( + "api/database" + "api/routes" + "log" + "os" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cors" +) + +func main() { + app := fiber.New(fiber.Config{ + StrictRouting: true, + DisableStartupMessage: true, + }) + + app.Use(cors.New(cors.Config{ + AllowCredentials: true, + })) + + if err := database.ConnectDB(); err != nil { + panic("Could not connect to database") + } + + routes.Setup(app) + + log.Println("Server started in port " + os.Getenv("API_PORT")) + app.Listen(":" + os.Getenv("API_PORT")) +} diff --git a/models/users.go b/models/users.go new file mode 100644 index 0000000..2b30a47 --- /dev/null +++ b/models/users.go @@ -0,0 +1,12 @@ +package models + +type User struct { + Id uint `gorm:"primary key" json:"id"` + Name string `gorm:"size:40;not null" json:"name"` + Email string `gorm:"size:40;not null;unique" json:"email"` + Password []byte `gorm:"size:100;not null;" json:"-"` + UserType string `gorm:"size:1;not null;default:U" json:"usertype"` + Blocked string `gorm:"size:1;not null;default:N" json:"blocked"` + First string `gorm:"size:1;not null;default:S" json:"first"` + Cancelled string `gorm:"size:1;not null;default:N" json:"cancelled"` +} diff --git a/routes/routes.go b/routes/routes.go new file mode 100644 index 0000000..14d262a --- /dev/null +++ b/routes/routes.go @@ -0,0 +1,19 @@ +package routes + +import ( + "api/controllers" + + "github.com/gofiber/fiber/v2" +) + +func Setup(app *fiber.App) { + + app.Post("/login", controllers.Login) + app.Post("/logout", controllers.Logout) + + app.Get("/user", controllers.User) + app.Get("/users", controllers.Users) + app.Post("/user", controllers.AddUser) + + app.Get("/", controllers.Hello) +} diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..bbe9e74 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,28 @@ +package utils + +import ( + "os" + + "github.com/dgrijalva/jwt-go" + "github.com/gofiber/fiber/v2" + "golang.org/x/crypto/bcrypt" +) + +func HashPassword(password string) ([]byte, error) { + return bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) +} + +func VerifyAuthentication(c *fiber.Ctx, cookie string) (*jwt.StandardClaims, error) { + + token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(os.Getenv("API_SECRET")), nil + }) + + if err != nil { + return nil, err + } + + claims := token.Claims.(*jwt.StandardClaims) + + return claims, nil +}