diff --git a/.env.example b/.env.example index c43b1ba..88fd3bd 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,4 @@ PORT=8080 mongoURI=mongodb://localhost:27017 -dbo=school \ No newline at end of file +dbo=bugbegone +secret=secretkey \ No newline at end of file diff --git a/controllers/authController.go b/controllers/authController.go index 0eee7c3..7a06c6d 100644 --- a/controllers/authController.go +++ b/controllers/authController.go @@ -2,21 +2,29 @@ package controllers import ( "context" - "encoding/base64" + "os" "time" "github.com/SowinskiBraeden/BugBeGone/database" "github.com/SowinskiBraeden/BugBeGone/models" + "github.com/google/uuid" + "github.com/joho/godotenv" + + "github.com/dgrijalva/jwt-go" + "github.com/gofiber/fiber/v2" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" ) var userCollection *mongo.Collection = database.OpenCollection(database.Client, "users") +var SecretKey string -func toBase64(b []byte) string { - return base64.StdEncoding.EncodeToString(b) +func Init() { + godotenv.Load(".env") + SecretKey = os.Getenv("secret") } func Register(c *fiber.Ctx) error { @@ -76,12 +84,15 @@ func Register(c *fiber.Ctx) error { }) } + user.UID = uuid.New().String() user.Username = username user.Firstname = firstname user.Lastname = lastname user.Email = email user.Password = user.HashPassword(password) user.TempPassword = false + user.Attempts = 0 + user.Disabled = false user.ID = primitive.NewObjectID() user.Created_at, _ = time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) user.Updated_at, _ = time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) @@ -103,5 +114,126 @@ func Register(c *fiber.Ctx) error { } func Login(c *fiber.Ctx) error { - return c.Status(fiber.StatusNotImplemented).JSON(fiber.Map{}) + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + username := c.FormValue("username") + password := c.FormValue("password") + + if username == "" && password == "" { + cancel() + return c.Status(fiber.StatusBadRequest).Render("login", fiber.Map{ + "msg": "", + "errorMsg": "Username and password can't be blank", + }) + } + if username == "" { + cancel() + return c.Status(fiber.StatusBadRequest).Render("login", fiber.Map{ + "msg": "", + "errorMsg": "Username can't be blank", + }) + } + if password == "" { + cancel() + return c.Status(fiber.StatusBadRequest).Render("login", fiber.Map{ + "msg": "", + "errorMsg": "Password can't be blank", + }) + } + + var user models.User + err := userCollection.FindOne(ctx, bson.M{"username": username}).Decode(&user) + defer cancel() + + if err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).Render("login", fiber.Map{ + "msg": "", + "errorMsg": "user not found", + }) + } + + var localAccountDisabled = false + if user.Attempts >= 5 { + localAccountDisabled = true // Catches newly disbaled account before student obj is updated + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "disabled": true, + "attempts": 0, + "updated_at": update_time, + }, + } + + _, updateErr := userCollection.UpdateOne( + ctx, + bson.M{"username": username}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).Render("login", fiber.Map{ + "msg": "", + "errorMsg": "the user could not be updated", + }) + } + } + + if localAccountDisabled || user.Disabled { + cancel() + return c.Status(fiber.StatusForbidden).Render("login", fiber.Map{ + "msg": "Account is Disabled, contact support", + "errorMsg": "Account is Disabled, contact support", + }) + } + + var verified bool = user.ComparePasswords(password) + if verified == false { + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "Attempts": user.Attempts + 1, + "updated_at": update_time, + }, + } + + _, updateErr := userCollection.UpdateOne( + ctx, + bson.M{"username": username}, + update, + ) + cancel() + if updateErr != nil { + return c.Status(fiber.StatusInternalServerError).Render("login", fiber.Map{ + "msg": "", + "errorMsg": "the student could not be updated", + }) + } + return c.Status(fiber.StatusBadRequest).Render("login", fiber.Map{ + "msg": "", + "errorMsg": "incorrect password", + }) + } + defer cancel() + + claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{ + Issuer: user.UID, + ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), // 1 Day + }) + token, err := claims.SignedString([]byte(SecretKey)) + if err != nil { + return c.Status(fiber.StatusInternalServerError).Render("login", fiber.Map{ + "msg": "", + "errorMsg": "could not log in", + }) + } + + cookie := fiber.Cookie{ + Name: "jwt", + Value: token, + Expires: time.Now().Add(time.Hour * 24), + HTTPOnly: true, + } + c.Cookie(&cookie) + + return c.Status(fiber.StatusNotImplemented).Render("dasboard", fiber.Map{}) } diff --git a/go.mod b/go.mod index bcb7e6b..84b09d0 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,10 @@ module github.com/SowinskiBraeden/BugBeGone go 1.17 require ( + github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/gofiber/fiber/v2 v2.31.0 github.com/gofiber/template v1.6.26 + github.com/google/uuid v1.1.2 github.com/joho/godotenv v1.4.0 go.mongodb.org/mongo-driver v1.8.4 golang.org/x/crypto v0.0.0-20220214200702-86341886e292 diff --git a/go.sum b/go.sum index 0fcf2c4..dd5da99 100644 --- a/go.sum +++ b/go.sum @@ -102,6 +102,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/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/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= diff --git a/main.go b/main.go index 2122805..6918f2d 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "html/template" "os" + "github.com/SowinskiBraeden/BugBeGone/controllers" "github.com/SowinskiBraeden/BugBeGone/routes" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" @@ -21,6 +22,8 @@ func main() { }, ) + controllers.Init() + app := fiber.New(fiber.Config{ Views: engine, }) diff --git a/models/userModel.go b/models/userModel.go index 2fc4737..31261d8 100644 --- a/models/userModel.go +++ b/models/userModel.go @@ -19,6 +19,7 @@ var ( type User struct { ID primitive.ObjectID `bson:"_id"` + UID string `json:"uid"` Firstname string `json:"firstname"` Lastname string `json:"lastname"` Username string `json:"username" validate:"required"` @@ -26,6 +27,8 @@ type User struct { Password string `json:"-" validate:"min=10,max=32"` TempPassword bool `json:"temppassword"` Subscription string `json:"subscription"` // standard, professional, business + Attempts int `json:"attempts"` + Disabled bool `json:""` Created_at time.Time `json:"created_at"` Updated_at time.Time `json:"updated_at"` }