diff --git a/internal/api/routes.go b/internal/api/routes.go index 1de8f66..c1a83e5 100644 --- a/internal/api/routes.go +++ b/internal/api/routes.go @@ -4,6 +4,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/pahmiudahgede/senggoldong/internal/controllers" "github.com/pahmiudahgede/senggoldong/internal/middleware" + "github.com/pahmiudahgede/senggoldong/utils" ) func AppRouter(app *fiber.App) { @@ -35,8 +36,8 @@ func AppRouter(app *fiber.App) { api.Delete("/coverage-areas-subdistrict/:id", controllers.DeleteCoverageSubdistrict) // # role # - api.Get("/roles", controllers.GetAllUserRoles) - api.Get("/role/:id", controllers.GetUserRoleByID) + api.Get("/roles", middleware.RoleRequired(utils.RoleAdministrator), controllers.GetAllUserRoles) + api.Get("/role/:id", middleware.RoleRequired(utils.RoleAdministrator), controllers.GetUserRoleByID) // # authentication # api.Post("/register", controllers.Register) diff --git a/internal/controllers/auth.go b/internal/controllers/auth.go index ce52701..a663570 100644 --- a/internal/controllers/auth.go +++ b/internal/controllers/auth.go @@ -70,7 +70,6 @@ func Login(c *fiber.Ctx) error { } if err := c.BodyParser(&credentials); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(utils.FormatResponse( fiber.StatusBadRequest, "Invalid input data", @@ -78,9 +77,17 @@ func Login(c *fiber.Ctx) error { )) } + user, err := repositories.GetUserByEmailOrUsername(credentials.EmailOrUsername) + if err != nil { + return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse( + fiber.StatusUnauthorized, + err.Error(), + nil, + )) + } + token, err := services.LoginUser(credentials.EmailOrUsername, credentials.Password) if err != nil { - return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse( fiber.StatusUnauthorized, err.Error(), @@ -91,7 +98,10 @@ func Login(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(utils.FormatResponse( fiber.StatusOK, "Login successful", - map[string]string{"token": token}, + map[string]interface{}{ + "token": token, + "role": user.RoleID, + }, )) } diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index 96c7748..13b44b8 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -1,13 +1,68 @@ package middleware import ( + "errors" "os" "strings" "github.com/gofiber/fiber/v2" "github.com/golang-jwt/jwt/v5" + "github.com/pahmiudahgede/senggoldong/utils" ) +func RoleRequired(roles ...string) fiber.Handler { + return func(c *fiber.Ctx) error { + + tokenString := c.Get("Authorization") + if tokenString == "" { + return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse( + fiber.StatusUnauthorized, + "Token is required", + nil, + )) + } + + tokenString = strings.TrimPrefix(tokenString, "Bearer ") + + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, errors.New("unexpected signing method") + } + return []byte(os.Getenv("API_KEY")), nil + }) + if err != nil || !token.Valid { + return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse( + fiber.StatusUnauthorized, + "Invalid or expired token", + nil, + )) + } + + claims, ok := token.Claims.(jwt.MapClaims) + if !ok { + return c.Status(fiber.StatusUnauthorized).JSON(utils.FormatResponse( + fiber.StatusUnauthorized, + "Invalid token claims", + nil, + )) + } + + role := claims["role"].(string) + + for _, r := range roles { + if r == role { + return c.Next() + } + } + + return c.Status(fiber.StatusForbidden).JSON(utils.FormatResponse( + fiber.StatusForbidden, + "You do not have permission to access this resource", + nil, + )) + } +} + func AuthMiddleware(c *fiber.Ctx) error { tokenString := c.Get("Authorization") tokenString = strings.TrimPrefix(tokenString, "Bearer ") diff --git a/internal/services/auth.go b/internal/services/auth.go index ce6f7fd..2630679 100644 --- a/internal/services/auth.go +++ b/internal/services/auth.go @@ -54,15 +54,15 @@ func LoginUser(emailOrUsername, password string) (string, error) { return "", errors.New("invalid email/username or password") } - token := generateJWT(user.ID) - + token := generateJWT(user.ID, user.RoleID) return token, nil } -func generateJWT(userID string) string { +func generateJWT(userID, role string) string { claims := jwt.MapClaims{ - "sub": userID, - "exp": time.Now().Add(time.Hour * 24).Unix(), + "sub": userID, + "role": role, + "exp": time.Now().Add(time.Hour * 24 * 7).Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) diff --git a/utils/role_permission.go b/utils/role_permission.go new file mode 100644 index 0000000..ac3ef25 --- /dev/null +++ b/utils/role_permission.go @@ -0,0 +1,8 @@ +package utils + +const ( + RoleMasyarakat = "3e8cd1f0-205c-488d-9bab-bb53b99a6f49" + RolePengepul = "88a756fd-e9ab-4257-83a6-c36a0fb944b1" + RolePengelola = "cb328d60-5d40-4404-bfe1-25224d1bd126" + RoleAdministrator = "229395a8-f26d-445b-ae53-c67e5b25ea03" +)