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() } }