Skip to content

Commit

Permalink
Merge branch 'dev' into feat/single-product
Browse files Browse the repository at this point in the history
Signed-off-by: Michael <[email protected]>
  • Loading branch information
michaelcosj authored Jul 25, 2024
2 parents 53b0cc5 + 4eba76b commit 37e39d9
Show file tree
Hide file tree
Showing 14 changed files with 373 additions and 39 deletions.
2 changes: 1 addition & 1 deletion internal/models/product.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
type Product struct {
ID string `gorm:"type:uuid;primaryKey" json:"product_id"`
Name string `gorm:"column:name; type:varchar(255); not null" json:"name"`
Price float64 `gorm:"column:price; type:decimal(10,2);not null; default:0" json:"price"`
Price float64 `gorm:"column:price; type:decimal(10,2);not null; default:0" json:"price"`
Description string `gorm:"column:description; type:text" json:"description"`
OwnerID string `gorm:"type:uuid;" json:"owner_id"`
Category []Category `gorm:"many2many:product_categories;;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"category"`
Expand Down
5 changes: 5 additions & 0 deletions internal/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ type LoginRequestModel struct {
Password string `json:"password" validate:"required"`
}

type ChangePasswordRequestModel struct {
OldPassword string `json:"old_password" validate:"required"`
NewPassword string `json:"new_password" validate:"required,min=7"`
}

func (u *User) AddUserToOrganisation(db *gorm.DB, user interface{}, orgs []interface{}) error {

// Add user to organisation
Expand Down
5 changes: 0 additions & 5 deletions pkg/controller/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,6 @@ func (base *Controller) VerifyResetToken(c *gin.Context) {

}

func (base *Controller) ChangePassword(c *gin.Context) {
// to be implemented

}

func (base *Controller) RequestMagicLink(c *gin.Context) {
// to be implemented

Expand Down
44 changes: 44 additions & 0 deletions pkg/controller/auth/password.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package auth

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/hngprojects/hng_boilerplate_golang_web/internal/models"
service "github.com/hngprojects/hng_boilerplate_golang_web/services/auth"
"github.com/hngprojects/hng_boilerplate_golang_web/utility"
)

func (base *Controller) ChangePassword(c *gin.Context) {
var (
req = models.ChangePasswordRequestModel{}
)

err := c.ShouldBind(&req)
if err != nil {
rd := utility.BuildErrorResponse(http.StatusBadRequest, "error", "Failed to parse request body", err, nil)
c.JSON(http.StatusBadRequest, rd)
return
}

err = base.Validator.Struct(&req)
if err != nil {
rd := utility.BuildErrorResponse(http.StatusUnprocessableEntity, "error", "Validation failed",
utility.ValidationResponse(err, base.Validator), nil)
c.JSON(http.StatusUnprocessableEntity, rd)
return
}

respData, code, err := service.UpdateUserPassword(c, req, base.Db.Postgresql)
if err != nil {
rd := utility.BuildErrorResponse(code, "error", err.Error(), err, nil)
c.JSON(code, rd)
return
}

base.Logger.Info("password changed successfully")

rd := utility.BuildSuccessResponse(http.StatusOK, "Password updated successfully", respData)
c.JSON(http.StatusOK, rd)

}
20 changes: 20 additions & 0 deletions pkg/middleware/jwttoken.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package middleware

import (
"errors"
"fmt"
"time"

"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
"gorm.io/gorm"

"github.com/hngprojects/hng_boilerplate_golang_web/internal/config"
"github.com/hngprojects/hng_boilerplate_golang_web/internal/models"
Expand Down Expand Up @@ -80,3 +83,20 @@ func TokenValid(bearerToken string) (*jwt.Token, error) {
}
return token, nil
}

func GetUserClaims(c *gin.Context, db *gorm.DB, theValue string) (interface{}, error) {

claims, exists := c.Get("userClaims")
if !exists {
return nil, errors.New("user claims not found")
}

userClaims := claims.(jwt.MapClaims)
userValue, ok := userClaims[theValue]
if !ok {
return nil, errors.New("invalid value")
}

return userValue, nil

}
6 changes: 4 additions & 2 deletions pkg/router/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/go-playground/validator/v10"

"github.com/hngprojects/hng_boilerplate_golang_web/external/request"
"github.com/hngprojects/hng_boilerplate_golang_web/internal/models"
"github.com/hngprojects/hng_boilerplate_golang_web/pkg/controller/auth"
"github.com/hngprojects/hng_boilerplate_golang_web/pkg/middleware"
"github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage"
Expand All @@ -28,10 +29,11 @@ func Auth(r *gin.Engine, ApiVersion string, validator *validator.Validate, db *s
authUrl.POST("/magick-link/verify", auth.VerifyMagicLink)
}

authUrlSec := r.Group(fmt.Sprintf("%v/auth", ApiVersion), middleware.Authorize(db.Postgresql))
authUrlSec := r.Group(fmt.Sprintf("%v/auth", ApiVersion),
middleware.Authorize(db.Postgresql, models.RoleIdentity.SuperAdmin, models.RoleIdentity.User))
{
authUrlSec.POST("/logout", auth.LogoutUser)
authUrlSec.POST("/change-password", auth.ChangePassword)
authUrlSec.PUT("/change-password", auth.ChangePassword)
}
return r
}
4 changes: 4 additions & 0 deletions services/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ func CreateAdmin(req models.CreateUserRequestModel, db *gorm.DB) (gin.H, int, er
"role": models.AdminRoleName,
"expires_in": tokenData.ExpiresAt.Unix(),
"access_token": tokenData.AccessToken,
"created_at": user.CreatedAt,
"updated_at": user.UpdatedAt,
}

return responseData, http.StatusCreated, nil
Expand Down Expand Up @@ -248,6 +250,8 @@ func LoginUser(req models.LoginRequestModel, db *gorm.DB) (gin.H, int, error) {
"role": userData.Role,
"expires_in": tokenData.ExpiresAt.Unix(),
"access_token": tokenData.AccessToken,
"created_at": userData.CreatedAt,
"updated_at": userData.UpdatedAt,
}

return responseData, http.StatusOK, nil
Expand Down
54 changes: 54 additions & 0 deletions services/auth/password.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package auth

import (
"errors"
"fmt"
"net/http"

"github.com/gin-gonic/gin"
"github.com/hngprojects/hng_boilerplate_golang_web/internal/models"
"github.com/hngprojects/hng_boilerplate_golang_web/pkg/middleware"
"github.com/hngprojects/hng_boilerplate_golang_web/utility"
"gorm.io/gorm"
)

func UpdateUserPassword(c *gin.Context, req models.ChangePasswordRequestModel, db *gorm.DB) (*models.User, int, error) {

user := models.User{}

userId, err := middleware.GetUserClaims(c, db, "user_id")
if err != nil {
return nil, http.StatusNotFound, err
}

userID, ok := userId.(string)
if !ok {
return nil, http.StatusBadRequest, errors.New("user_id is not of type string")
}

userDataExist, err := user.GetUserByID(db, userID)
if err != nil {
return nil, http.StatusNotFound, fmt.Errorf("unable to fetch user " + err.Error())
}

if !utility.CompareHash(req.OldPassword, userDataExist.Password) {
return nil, http.StatusBadRequest, fmt.Errorf("old password is incorrect")
}

if req.OldPassword == req.NewPassword {
return nil, http.StatusConflict, errors.New("new password cannot be the same as the old password")
}

hashedPassword, err := utility.HashPassword(req.NewPassword)
if err != nil {
return nil, http.StatusBadRequest, err
}

userDataExist.Password = hashedPassword
err = userDataExist.Update(db)
if err != nil {
return nil, http.StatusBadRequest, err
}

return &userDataExist, http.StatusOK, nil
}
16 changes: 16 additions & 0 deletions tests/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,22 @@ func AssertBool(t *testing.T, got, expected bool) {
}
}

func AssertValidationError(t *testing.T, response map[string]interface{}, field string, expectedMessage string) {
errors, ok := response["error"].(map[string]interface{})
if !ok {
t.Fatalf("expected 'error' field in response")
}

errorMessage, exists := errors[field]
if !exists {
t.Fatalf("expected validation error message for field '%s'", field)
}

if errorMessage != expectedMessage {
t.Errorf("unexpected error message for field '%s': got %v, want %v", field, errorMessage, expectedMessage)
}
}

// helper to signup a user
func SignupUser(t *testing.T, r *gin.Engine, auth auth.Controller, userSignUpData models.CreateUserRequestModel) {
var (
Expand Down
4 changes: 3 additions & 1 deletion tests/test_auth/auth_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package test_waitlist
package test_auth

import (
"bytes"
Expand Down Expand Up @@ -134,6 +134,7 @@ func TestUserSignup(t *testing.T) {
}

}

// test admin signup
func TestAdminSignup(t *testing.T) {
logger := tst.Setup()
Expand Down Expand Up @@ -352,6 +353,7 @@ func TestLogin(t *testing.T) {
}

}

// test user logout
func TestLogout(t *testing.T) {
logger := tst.Setup()
Expand Down
35 changes: 35 additions & 0 deletions tests/test_auth/base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package test_auth

import (
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/hngprojects/hng_boilerplate_golang_web/internal/models"
"github.com/hngprojects/hng_boilerplate_golang_web/pkg/controller/auth"
"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/tests"
)

func SetupAuthTestRouter() (*gin.Engine, *auth.Controller) {
gin.SetMode(gin.TestMode)

logger := tests.Setup()
db := storage.Connection()
validator := validator.New()

authController := &auth.Controller{
Db: db,
Validator: validator,
Logger: logger,
}

r := gin.Default()
SetupAuthRoutes(r, authController)
return r, authController
}

func SetupAuthRoutes(r *gin.Engine, userController *auth.Controller) {
r.PUT("/api/v1/auth/change-password",
middleware.Authorize(userController.Db.Postgresql, models.RoleIdentity.SuperAdmin, models.RoleIdentity.User),
userController.ChangePassword)
}
Loading

0 comments on commit 37e39d9

Please sign in to comment.