diff --git a/controllers/user.go b/controllers/user.go new file mode 100644 index 0000000..ad5081e --- /dev/null +++ b/controllers/user.go @@ -0,0 +1,77 @@ +package controllers + +import ( + "os" + "time" + + "git.gnous.eu/Rick/calendrier/models" + "git.gnous.eu/Rick/calendrier/services" + "github.com/gofiber/fiber/v2" + "github.com/golang-jwt/jwt" + "golang.org/x/crypto/bcrypt" +) + +// @Summary Créer un nouvel utilisateur +// @Tag user +// @Param user body models.User true "L'utilisateur à créer" +// @Success 200 int +// @Failure 400 "Mauvaise structure" +// @Failure 401 "Token mal formaté" +// @Failure 500 "Erreur dans la base de données" +// @Router /user [post] +func CreateUser(c *fiber.Ctx) error { + tmp := new(models.User) + err := c.BodyParser(tmp) + + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"err": err.Error()}) + } + + err = tmp.HashPassword() + + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"err": err.Error()}) + } + + err = services.CreateUser(tmp) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"err": err.Error()}) + } + + return c.SendStatus(fiber.StatusOK) +} + +// @Summary Connexion +// @Tag user +// @Param user body models.User true "L'utilisateur voulant se connecter" +// @Success 200 string +// @Failure 401 "Mauvais mot de passe" +// @Failure 500 "Erreur dans la base de données" +// @Router /login [post] +func GetToken(c *fiber.Ctx) error { + log := new(models.User) + err := c.BodyParser(log) + + tmp, err := services.GetUserByName(log.Name) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"err": err.Error()}) + } + + err = bcrypt.CompareHashAndPassword([]byte(tmp.Password), []byte(log.Password)) + if err != nil { + return c.SendStatus(fiber.StatusUnauthorized) + } else { + claim := jwt.MapClaims{ + "name": tmp.Name, + "exp": time.Now().Add(time.Hour * 72).Unix(), + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim) + t, err := token.SignedString([]byte(os.Getenv("JWT_SECRET"))) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"err": err.Error()}) + } else { + return c.Status(fiber.StatusOK).JSON(fiber.Map{"token": t}) + } + } +} diff --git a/go.mod b/go.mod index 927e0c4..9ac9688 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,11 @@ go 1.21.5 require ( github.com/gofiber/fiber/v2 v2.52.0 + github.com/golang-jwt/jwt v3.2.2+incompatible github.com/uptrace/bun v1.1.17 github.com/uptrace/bun/dialect/pgdialect v1.1.17 github.com/uptrace/bun/driver/pgdriver v1.1.17 + golang.org/x/crypto v0.18.0 ) require ( @@ -24,7 +26,6 @@ require ( github.com/valyala/tcplisten v1.0.0 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - golang.org/x/crypto v0.18.0 // indirect golang.org/x/sys v0.16.0 // indirect mellium.im/sasl v0.3.1 // indirect ) diff --git a/go.sum b/go.sum index f5ed533..97317ac 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE= github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= diff --git a/models/user.go b/models/user.go new file mode 100644 index 0000000..1afe77c --- /dev/null +++ b/models/user.go @@ -0,0 +1,20 @@ +package models + +import ( + "github.com/uptrace/bun" + "golang.org/x/crypto/bcrypt" +) + +type User struct { + bun.BaseModel `bun:"table:c_user"` + Id int `json:"id" bun:"id,pk,autoincrement"` + Name string `json:"name"` + Email string `json:"email"` + Password string `json:"password"` +} + +func (u *User) HashPassword() error { + tmp, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost) + u.Password = string(tmp) + return err +} diff --git a/routes/api.go b/routes/api.go index 3697c45..f85e20e 100644 --- a/routes/api.go +++ b/routes/api.go @@ -6,6 +6,12 @@ import ( ) func SetupApi(app *fiber.App) { + /* + api := app.Group("/api/v1", jwtware.New(jwtware.Config{ + SigningKey: jwtware.SigningKey{Key: []byte(os.Getenv("JWT_SECRET"))}, + })) + */ + api := app.Group("/api/v1") api.Get("/calendars", controllers.GetCalendars) @@ -13,6 +19,9 @@ func SetupApi(app *fiber.App) { api.Post("/calendar", controllers.PostCalendar) + api.Post("/user", controllers.CreateUser) + api.Post("/login", controllers.GetToken) + /* api.Put("/calendar/:id/visibility", controllers.PutVisibilityCalendar) api.Delete("/calendar/:id", controllers.DeleteCalendar) diff --git a/services/user.go b/services/user.go new file mode 100644 index 0000000..85b3c63 --- /dev/null +++ b/services/user.go @@ -0,0 +1,26 @@ +package services + +import ( + "context" + + "git.gnous.eu/Rick/calendrier/models" +) + +func CreateUser(c *models.User) error { + ctx := context.Background() + db := get() + + _, err := db.NewInsert().Model(c).Exec(ctx) + + return err +} + +func GetUserByName(n string) (models.User, error) { + var res models.User + ctx := context.Background() + db := get() + + err := db.NewSelect().Table("c_user").Where("name = ?", n).Scan(ctx, &res) + + return res, err +} diff --git a/sql/init.sql b/sql/init.sql index 89236d0..e2c39ec 100644 --- a/sql/init.sql +++ b/sql/init.sql @@ -4,8 +4,8 @@ CREATE DATABASE r_calendar ENCODING 'UTF-8'; CREATE TABLE IF NOT EXISTS c_user ( id SERIAL PRIMARY KEY NOT NULL, - name TEXT NOT NULL, - email TEXT, + name TEXT NOT NULL UNIQUE, + email TEXT UNIQUE, password TEXT NOT NULL );