kontenhumas-be/app/middleware/module_access.middleware.go

296 lines
8.3 KiB
Go
Raw Normal View History

2026-01-15 09:04:49 +00:00
package middleware
import (
"netidhub-saas-be/app/database"
"netidhub-saas-be/app/database/entity"
"github.com/gofiber/fiber/v2"
)
type ModuleAccessMiddleware struct {
DB *database.Database
}
func NewModuleAccessMiddleware(db *database.Database) *ModuleAccessMiddleware {
return &ModuleAccessMiddleware{
DB: db,
}
}
// CheckModuleAccess middleware untuk validasi akses user_level ke modul tertentu
// Menggunakan module_id atau path_url sebagai identifier
func (m *ModuleAccessMiddleware) CheckModuleAccess(moduleIdentifier interface{}) fiber.Handler {
return func(c *fiber.Ctx) error {
// Get user from context (diasumsikan sudah ada middleware auth yang set user ke context)
userCtx := c.Locals("user")
if userCtx == nil {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
"code": 401,
"messages": []string{"User tidak terautentikasi"},
})
}
// Cast user dari context
user, ok := userCtx.(*entity.Users)
if !ok || user == nil {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
"code": 401,
"messages": []string{"User tidak valid"},
})
}
// Get user role untuk mendapatkan user_level_id
var userRole entity.UserRoles
if err := m.DB.DB.Where("id = ?", user.UserRoleId).First(&userRole).Error; err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"code": 500,
"messages": []string{"Error mendapatkan user role"},
"error": err.Error(),
})
}
userLevelId := userRole.UserLevelId
// Dapatkan module berdasarkan identifier (bisa module_id atau path_url)
var module entity.MasterModules
var err error
switch v := moduleIdentifier.(type) {
case uint:
// Jika moduleIdentifier adalah ID
err = m.DB.DB.Where("id = ? AND is_active = ?", v, true).First(&module).Error
case string:
// Jika moduleIdentifier adalah path_url
err = m.DB.DB.Where("path_url = ? AND is_active = ?", v, true).First(&module).Error
default:
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"code": 400,
"messages": []string{"Module identifier tidak valid"},
})
}
if err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"success": false,
"code": 404,
"messages": []string{"Module tidak ditemukan"},
})
}
// Check akses user_level ke module
var access entity.UserLevelModuleAccesses
err = m.DB.DB.Where(
"user_level_id = ? AND module_id = ? AND is_active = ?",
userLevelId,
module.ID,
true,
).First(&access).Error
if err != nil {
// Jika tidak ada record, berarti tidak ada akses
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
"success": false,
"code": 403,
"messages": []string{"Anda tidak memiliki akses ke modul ini"},
"user_level_id": userLevelId,
"module_id": module.ID,
"module_name": module.Name,
})
}
if !access.CanAccess {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
"success": false,
"code": 403,
"messages": []string{"Akses ke modul ini ditolak"},
"user_level_id": userLevelId,
"module_id": module.ID,
"module_name": module.Name,
})
}
// Set module ke context untuk digunakan di handler
c.Locals("module", &module)
c.Locals("user_level_id", userLevelId)
return c.Next()
}
}
// CheckModuleAccessByPath middleware untuk validasi akses berdasarkan path yang sedang diakses
// Akan otomatis mencocokkan path dengan module.path_url
func (m *ModuleAccessMiddleware) CheckModuleAccessByPath() fiber.Handler {
return func(c *fiber.Ctx) error {
// Get user from context
userCtx := c.Locals("user")
if userCtx == nil {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
"code": 401,
"messages": []string{"User tidak terautentikasi"},
})
}
user, ok := userCtx.(*entity.Users)
if !ok || user == nil {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
"code": 401,
"messages": []string{"User tidak valid"},
})
}
// Get user role untuk mendapatkan user_level_id
var userRole entity.UserRoles
if err := m.DB.DB.Where("id = ?", user.UserRoleId).First(&userRole).Error; err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"code": 500,
"messages": []string{"Error mendapatkan user role"},
"error": err.Error(),
})
}
userLevelId := userRole.UserLevelId
currentPath := c.Path()
// Cari module berdasarkan path_url yang cocok
var module entity.MasterModules
err := m.DB.DB.Where("path_url = ? AND is_active = ?", currentPath, true).First(&module).Error
if err != nil {
// Jika module tidak ditemukan, bisa jadi path ini tidak perlu validasi modul
// Atau bisa langsung return error tergantung kebijakan
return c.Next() // Skip validation jika module tidak ditemukan
}
// Check akses user_level ke module
var access entity.UserLevelModuleAccesses
err = m.DB.DB.Where(
"user_level_id = ? AND module_id = ? AND is_active = ?",
userLevelId,
module.ID,
true,
).First(&access).Error
if err != nil || !access.CanAccess {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
"success": false,
"code": 403,
"messages": []string{"Anda tidak memiliki akses ke halaman ini"},
"user_level_id": userLevelId,
"path": currentPath,
"module_name": module.Name,
})
}
// Set module ke context
c.Locals("module", &module)
c.Locals("user_level_id", userLevelId)
return c.Next()
}
}
// CheckMenuAccess middleware untuk validasi akses user_level ke menu beserta modul-modulnya
func (m *ModuleAccessMiddleware) CheckMenuAccess(menuId uint) fiber.Handler {
return func(c *fiber.Ctx) error {
// Get user from context
userCtx := c.Locals("user")
if userCtx == nil {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
"code": 401,
"messages": []string{"User tidak terautentikasi"},
})
}
user, ok := userCtx.(*entity.Users)
if !ok || user == nil {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
"code": 401,
"messages": []string{"User tidak valid"},
})
}
// Get user role untuk mendapatkan user_level_id
var userRole entity.UserRoles
if err := m.DB.DB.Where("id = ?", user.UserRoleId).First(&userRole).Error; err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"code": 500,
"messages": []string{"Error mendapatkan user role"},
"error": err.Error(),
})
}
userLevelId := userRole.UserLevelId
// Get menu
var menu entity.MasterMenus
if err := m.DB.DB.Where("id = ? AND is_active = ?", menuId, true).First(&menu).Error; err != nil {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"success": false,
"code": 404,
"messages": []string{"Menu tidak ditemukan"},
})
}
// Get semua modul yang ada di menu ini
var menuModules []entity.MenuModules
if err := m.DB.DB.Where("menu_id = ? AND is_active = ?", menuId, true).Find(&menuModules).Error; err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"code": 500,
"messages": []string{"Error mendapatkan menu modules"},
"error": err.Error(),
})
}
if len(menuModules) == 0 {
// Jika menu tidak punya modul, skip validasi
return c.Next()
}
// Check apakah user_level memiliki akses ke minimal satu modul di menu ini
hasAccess := false
for _, menuModule := range menuModules {
var access entity.UserLevelModuleAccesses
err := m.DB.DB.Where(
"user_level_id = ? AND module_id = ? AND is_active = ? AND can_access = ?",
userLevelId,
menuModule.ModuleId,
true,
true,
).First(&access).Error
if err == nil {
hasAccess = true
break
}
}
if !hasAccess {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{
"success": false,
"code": 403,
"messages": []string{"Anda tidak memiliki akses ke menu ini"},
"user_level_id": userLevelId,
"menu_id": menuId,
"menu_name": menu.Name,
})
}
// Set menu ke context
c.Locals("menu", &menu)
c.Locals("user_level_id", userLevelId)
return c.Next()
}
}