From 046cf69678294be5da23f86bc3f8b8a1011f39bf Mon Sep 17 00:00:00 2001 From: Emmanuel Omoiya Date: Sun, 21 Jul 2024 16:10:32 +0100 Subject: [PATCH] feat: add fetch customers list --- .gitignore | 2 ++ internal/models/seed/seed.go | 20 ++++++++-------- internal/models/user.go | 12 ++++++++++ pkg/controller/user/user.go | 14 +++++++++++ pkg/middleware/authorize.go | 23 ++++++++++++++++++- pkg/middleware/jwttoken.go | 2 ++ .../storage/postgresql/connection.go | 2 +- pkg/router/organisation.go | 2 +- pkg/router/user.go | 2 ++ services/user/user.go | 19 +++++++++++++++ 10 files changed, 86 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 42092c91..df7969d6 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ _ignore/ app.env tmp/ .idea/ + +app.env \ No newline at end of file diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go index 728904a4..c2497e75 100644 --- a/internal/models/seed/seed.go +++ b/internal/models/seed/seed.go @@ -15,10 +15,11 @@ func SeedDatabase(db *gorm.DB) { Userid1 := utility.GenerateUUID() user1 := models.User{ - ID: Userid1, - Name: "John Doe", - Email: "john@example.com", + ID: Userid1, + Name: "John Doe", + Email: "john@example.com", Password: utility.RandomString(20), + Role: "customer", Profile: models.Profile{ ID: utility.GenerateUUID(), FirstName: "John", @@ -34,10 +35,11 @@ func SeedDatabase(db *gorm.DB) { Userid2 := utility.GenerateUUID() user2 := models.User{ - ID: Userid2, - Name: "Jane Doe", + ID: Userid2, + Name: "Jane Doe", Password: utility.RandomString(20), - Email: "jane@example.com", + Email: "jane@example.com", + Role: "customer", Profile: models.Profile{ ID: utility.GenerateUUID(), FirstName: "Jane", @@ -52,9 +54,9 @@ func SeedDatabase(db *gorm.DB) { } organisations := []models.Organisation{ - {ID: utility.GenerateUUID(), Name: "Org1", Email: fmt.Sprintf(utility.RandomString(4)+"@email.com"),Description: "Description1", OwnerID: Userid1}, - {ID: utility.GenerateUUID(), Name: "Org2", Email: fmt.Sprintf(utility.RandomString(4)+"@email.com"),Description: "Description2", OwnerID: Userid1}, - {ID: utility.GenerateUUID(), Name: "Org3", Email: fmt.Sprintf(utility.RandomString(4)+"@email.com"),Description: "Description3", OwnerID: Userid2}, + {ID: utility.GenerateUUID(), Name: "Org1", Email: fmt.Sprintf(utility.RandomString(4) + "@email.com"), Description: "Description1", OwnerID: Userid1}, + {ID: utility.GenerateUUID(), Name: "Org2", Email: fmt.Sprintf(utility.RandomString(4) + "@email.com"), Description: "Description2", OwnerID: Userid1}, + {ID: utility.GenerateUUID(), Name: "Org3", Email: fmt.Sprintf(utility.RandomString(4) + "@email.com"), Description: "Description3", OwnerID: Userid2}, } var existingUser models.User diff --git a/internal/models/user.go b/internal/models/user.go index b9f26a1a..cf63b5d6 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -13,6 +13,8 @@ type User struct { Name string `gorm:"column:name; type:varchar(255)" json:"name"` Email string `gorm:"column:email; type:varchar(255)" json:"email"` Password string `gorm:"column:password; type:text; not null" json:"-"` + Role string `gorm:"column:role; type:varchar(255)" json:"role"` + IsActive bool `gorm:"column:is_active; type:boolean" json:"is_active"` Profile Profile `gorm:"foreignKey:Userid;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"profile"` Organisations []Organisation `gorm:"many2many:user_organisations;;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"organisations" ` // many to many relationship Products []Product `gorm:"foreignKey:OwnerID" json:"products"` @@ -27,6 +29,7 @@ type CreateUserRequestModel struct { LastName string `json:"last_name" validate:"required"` UserName string `json:"username" validate:"required"` PhoneNumber string `json:"phone_number"` + Role string `json:"role"` } type LoginRequestModel struct { @@ -65,3 +68,12 @@ func (u *User) CreateUser(db *gorm.DB) error { return nil } + +func (u *User) GetAllCustomers(db *gorm.DB) ([]User, error) { + var users []User + if err := db.Preload("Profile").Preload("Products").Preload("Organisations").Where("role = ?", "customer").Find(&users).Error; err != nil { + return users, err + } + + return users, nil +} diff --git a/pkg/controller/user/user.go b/pkg/controller/user/user.go index 5cefd51f..fc7239fa 100644 --- a/pkg/controller/user/user.go +++ b/pkg/controller/user/user.go @@ -92,3 +92,17 @@ func (base *Controller) LoginUser(c *gin.Context) { rd := utility.BuildSuccessResponse(http.StatusOK, "user login successfully", respData) c.JSON(http.StatusOK, rd) } + +func (base *Controller) GetAllCustomers(c *gin.Context) { + respData, err := user.GetAllCustomers(base.Db.Postgresql) + if err != nil { + rd := utility.BuildErrorResponse(400, "error", err.Error(), err, nil) + c.JSON(http.StatusBadRequest, rd) + return + } + + base.Logger.Info("All Customers fetched successfully") + + rd := utility.BuildSuccessResponse(http.StatusOK, "All Customers fetched successfully", respData) + c.JSON(http.StatusOK, rd) +} diff --git a/pkg/middleware/authorize.go b/pkg/middleware/authorize.go index f4fa4ce7..7acfee75 100644 --- a/pkg/middleware/authorize.go +++ b/pkg/middleware/authorize.go @@ -10,7 +10,7 @@ import ( "github.com/hngprojects/hng_boilerplate_golang_web/utility" ) -func Authorize() gin.HandlerFunc { +func Authorize(requiredRoles ...string) gin.HandlerFunc { return func(c *gin.Context) { var tokenStr string bearerToken := c.GetHeader("Authorization") @@ -49,6 +49,27 @@ func Authorize() gin.HandlerFunc { return } + // Check user role + userRole, ok := claims["user_role"].(string) + if !ok { + c.AbortWithStatusJSON(http.StatusForbidden, utility.BuildErrorResponse(http.StatusForbidden, "error", "Forbidden", "Unauthorized", nil)) + return + } + + // Check if user role is in the list of required roles + roleAuthorized := false + for _, role := range requiredRoles { + if userRole == role { + roleAuthorized = true + break + } + } + + if !roleAuthorized { + c.AbortWithStatusJSON(http.StatusForbidden, utility.BuildErrorResponse(http.StatusForbidden, "error", "Forbidden", "Unauthorized", nil)) + return + } + // store user claims in Context // for accesiblity in controller diff --git a/pkg/middleware/jwttoken.go b/pkg/middleware/jwttoken.go index 9a9269d4..9c5bb9e0 100644 --- a/pkg/middleware/jwttoken.go +++ b/pkg/middleware/jwttoken.go @@ -20,10 +20,12 @@ func CreateToken(user models.User) (string, time.Time, error) { //create token userid := user.ID + userRole := user.Role userClaims := jwt.MapClaims{} // specify user claims userClaims["user_id"] = userid + userClaims["user_role"] = userRole userClaims["exp"] = UnixExp userClaims["authorised"] = true diff --git a/pkg/repository/storage/postgresql/connection.go b/pkg/repository/storage/postgresql/connection.go index feeb5f5e..e37ae8c5 100644 --- a/pkg/repository/storage/postgresql/connection.go +++ b/pkg/repository/storage/postgresql/connection.go @@ -44,7 +44,7 @@ func connectToDb(host, user, password, dbname, port, sslmode, timezone string, l port = detectedPort } - dsn := fmt.Sprintf("host=%v user=%v password=%v dbname=%v port=%v sslmode=%v TimeZone=%v", host, user, password, dbname, port, sslmode, timezone) + dsn := fmt.Sprintf("host=%v user=%v password=%v dbname=%v port=%v sslmode=%v TimeZone=%v prefer_simple_protocol=true", host, user, password, dbname, port, sslmode, timezone) newLogger := lg.New( log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer diff --git a/pkg/router/organisation.go b/pkg/router/organisation.go index 0a25a984..4c5a80de 100644 --- a/pkg/router/organisation.go +++ b/pkg/router/organisation.go @@ -17,7 +17,7 @@ func Organisation(r *gin.Engine, ApiVersion string, validator *validator.Validat extReq := request.ExternalRequest{Logger: logger, Test: false} organisation := organisation.Controller{Db: db, Validator: validator, Logger: logger, ExtReq: extReq} - organisationUrl := r.Group(fmt.Sprintf("%v", ApiVersion), middleware.Authorize()) + organisationUrl := r.Group(fmt.Sprintf("%v", ApiVersion), middleware.Authorize("customer", "admin")) { organisationUrl.POST("/organisations", organisation.CreateOrganisation) diff --git a/pkg/router/user.go b/pkg/router/user.go index 208030e0..fd016c55 100644 --- a/pkg/router/user.go +++ b/pkg/router/user.go @@ -8,6 +8,7 @@ import ( "github.com/hngprojects/hng_boilerplate_golang_web/external/request" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/controller/user" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/middleware" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" "github.com/hngprojects/hng_boilerplate_golang_web/utility" ) @@ -20,6 +21,7 @@ func User(r *gin.Engine, ApiVersion string, validator *validator.Validate, db *s { userUrl.POST("/users/signup", user.CreateUser) userUrl.POST("/users/login", user.LoginUser) + userUrl.GET("/customers", middleware.Authorize("admin"), user.GetAllCustomers) } return r } diff --git a/services/user/user.go b/services/user/user.go index 40ba39c9..0051ff64 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -71,6 +71,7 @@ func CreateUser(req models.CreateUserRequestModel, db *gorm.DB) (gin.H, int, err username = strings.ToLower(req.UserName) phoneNumber = req.PhoneNumber password = req.Password + role = req.Role responseData gin.H ) @@ -79,11 +80,16 @@ func CreateUser(req models.CreateUserRequestModel, db *gorm.DB) (gin.H, int, err return nil, http.StatusInternalServerError, err } + if role == "" { + role = "customer" + } + user := models.User{ ID: utility.GenerateUUID(), Name: username, Email: email, Password: password, + Role: role, Profile: models.Profile{ ID: utility.GenerateUUID(), FirstName: firstName, @@ -108,6 +114,7 @@ func CreateUser(req models.CreateUserRequestModel, db *gorm.DB) (gin.H, int, err "first_name": user.Profile.FirstName, "last_name": user.Profile.LastName, "phone": user.Profile.Phone, + "role": user.Role, "expires_in": expiry, "access_token": token, } @@ -149,9 +156,21 @@ func LoginUser(req models.LoginRequestModel, db *gorm.DB) (gin.H, int, error) { "first_name": userData.Profile.FirstName, "last_name": userData.Profile.LastName, "phone": userData.Profile.Phone, + "role": userData.Role, "expires_in": expiry, "access_token": token, } return responseData, http.StatusCreated, nil } + +func GetAllCustomers(db *gorm.DB) ([]models.User, error) { + var users models.User + + userResp, err := users.GetAllCustomers(db) + if err != nil { + return userResp, err + } + + return userResp, err +}