408 lines
12 KiB
Go
408 lines
12 KiB
Go
package middleware
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// MockRoleService est un mock du RoleService pour les tests RBAC
|
|
// Implémente l'interface RoleChecker pour être compatible avec RequireRole
|
|
type MockRoleService struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *MockRoleService) HasRole(ctx context.Context, userID int64, roleName string) (bool, error) {
|
|
args := m.Called(ctx, userID, roleName)
|
|
return args.Bool(0), args.Error(1)
|
|
}
|
|
|
|
func (m *MockRoleService) HasPermission(ctx context.Context, userID int64, resource, action string) (bool, error) {
|
|
args := m.Called(ctx, userID, resource, action)
|
|
return args.Bool(0), args.Error(1)
|
|
}
|
|
|
|
func TestRequireRole_WithValidRole(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
mockRoleService := new(MockRoleService)
|
|
mockRoleService.On("HasRole", mock.Anything, int64(123), "admin").Return(true, nil)
|
|
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", int64(123))
|
|
c.Next()
|
|
})
|
|
router.Use(RequireRole(mockRoleService, "admin"))
|
|
router.GET("/test", func(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
mockRoleService.AssertExpectations(t)
|
|
}
|
|
|
|
func TestRequireRole_WithInvalidRole(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
mockRoleService := new(MockRoleService)
|
|
mockRoleService.On("HasRole", mock.Anything, int64(123), "admin").Return(false, nil)
|
|
|
|
handlerCalled := false
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", int64(123))
|
|
c.Next()
|
|
})
|
|
router.Use(RequireRole(mockRoleService, "admin"))
|
|
router.GET("/test", func(c *gin.Context) {
|
|
handlerCalled = true
|
|
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusForbidden, w.Code)
|
|
assert.False(t, handlerCalled, "Handler should not be called")
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
// P0: Nouveau format AppError
|
|
errorObj, ok := response["error"].(map[string]interface{})
|
|
require.True(t, ok, "Error should be a map")
|
|
assert.Equal(t, "insufficient permissions", errorObj["message"])
|
|
|
|
mockRoleService.AssertExpectations(t)
|
|
}
|
|
|
|
func TestRequireRole_WithoutUserID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
mockRoleService := new(MockRoleService)
|
|
handlerCalled := false
|
|
router := gin.New()
|
|
router.Use(RequireRole(mockRoleService, "admin"))
|
|
router.GET("/test", func(c *gin.Context) {
|
|
handlerCalled = true
|
|
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
|
w := httptest.NewRecorder()
|
|
// user_id not set
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
assert.False(t, handlerCalled, "Handler should not be called")
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
// P0: Nouveau format AppError
|
|
errorObj, ok := response["error"].(map[string]interface{})
|
|
require.True(t, ok, "Error should be a map")
|
|
assert.Equal(t, "unauthorized", errorObj["message"])
|
|
|
|
mockRoleService.AssertNotCalled(t, "HasRole")
|
|
}
|
|
|
|
func TestRequireRole_WithServiceError(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
mockRoleService := new(MockRoleService)
|
|
mockRoleService.On("HasRole", mock.Anything, int64(123), "admin").Return(false, assert.AnError)
|
|
|
|
handlerCalled := false
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", int64(123))
|
|
c.Next()
|
|
})
|
|
router.Use(RequireRole(mockRoleService, "admin"))
|
|
router.GET("/test", func(c *gin.Context) {
|
|
handlerCalled = true
|
|
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.False(t, handlerCalled, "Handler should not be called")
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response, "error")
|
|
|
|
mockRoleService.AssertExpectations(t)
|
|
}
|
|
|
|
func TestRequireRole_WithIntUserID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
mockRoleService := new(MockRoleService)
|
|
mockRoleService.On("HasRole", mock.Anything, int64(123), "admin").Return(true, nil)
|
|
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", 123) // int instead of int64
|
|
c.Next()
|
|
})
|
|
router.Use(RequireRole(mockRoleService, "admin"))
|
|
router.GET("/test", func(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
mockRoleService.AssertExpectations(t)
|
|
}
|
|
|
|
func TestRequirePermission_WithValidPermission(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
mockRoleService := new(MockRoleService)
|
|
mockRoleService.On("HasPermission", mock.Anything, int64(123), "tracks", "create").Return(true, nil)
|
|
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", int64(123))
|
|
c.Next()
|
|
})
|
|
router.Use(RequirePermission(mockRoleService, "tracks", "create"))
|
|
router.POST("/test", func(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/test", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
mockRoleService.AssertExpectations(t)
|
|
}
|
|
|
|
func TestRequirePermission_WithInvalidPermission(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
mockRoleService := new(MockRoleService)
|
|
mockRoleService.On("HasPermission", mock.Anything, int64(123), "tracks", "delete").Return(false, nil)
|
|
|
|
handlerCalled := false
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", int64(123))
|
|
c.Next()
|
|
})
|
|
router.Use(RequirePermission(mockRoleService, "tracks", "delete"))
|
|
router.DELETE("/test", func(c *gin.Context) {
|
|
handlerCalled = true
|
|
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodDelete, "/test", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusForbidden, w.Code)
|
|
assert.False(t, handlerCalled, "Handler should not be called")
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
// P0: Nouveau format AppError
|
|
errorObj, ok := response["error"].(map[string]interface{})
|
|
require.True(t, ok, "Error should be a map")
|
|
assert.Equal(t, "insufficient permissions", errorObj["message"])
|
|
|
|
mockRoleService.AssertExpectations(t)
|
|
}
|
|
|
|
func TestRequirePermission_WithoutUserID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
mockRoleService := new(MockRoleService)
|
|
handlerCalled := false
|
|
router := gin.New()
|
|
router.Use(RequirePermission(mockRoleService, "tracks", "create"))
|
|
router.POST("/test", func(c *gin.Context) {
|
|
handlerCalled = true
|
|
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/test", nil)
|
|
w := httptest.NewRecorder()
|
|
// user_id not set
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
assert.False(t, handlerCalled, "Handler should not be called")
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
// P0: Nouveau format AppError
|
|
errorObj, ok := response["error"].(map[string]interface{})
|
|
require.True(t, ok, "Error should be a map")
|
|
assert.Equal(t, "unauthorized", errorObj["message"])
|
|
|
|
mockRoleService.AssertNotCalled(t, "HasPermission")
|
|
}
|
|
|
|
func TestRequirePermission_WithServiceError(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
mockRoleService := new(MockRoleService)
|
|
mockRoleService.On("HasPermission", mock.Anything, int64(123), "tracks", "create").Return(false, assert.AnError)
|
|
|
|
handlerCalled := false
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", int64(123))
|
|
c.Next()
|
|
})
|
|
router.Use(RequirePermission(mockRoleService, "tracks", "create"))
|
|
router.POST("/test", func(c *gin.Context) {
|
|
handlerCalled = true
|
|
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/test", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
assert.False(t, handlerCalled, "Handler should not be called")
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response, "error")
|
|
|
|
mockRoleService.AssertExpectations(t)
|
|
}
|
|
|
|
func TestRequirePermission_WithIntUserID(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
mockRoleService := new(MockRoleService)
|
|
mockRoleService.On("HasPermission", mock.Anything, int64(123), "users", "manage").Return(true, nil)
|
|
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", 123) // int instead of int64
|
|
c.Next()
|
|
})
|
|
router.Use(RequirePermission(mockRoleService, "users", "manage"))
|
|
router.GET("/test", func(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
mockRoleService.AssertExpectations(t)
|
|
}
|
|
|
|
func TestRequirePermission_WithInvalidUserIDType(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
mockRoleService := new(MockRoleService)
|
|
handlerCalled := false
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", "invalid") // Invalid type
|
|
c.Next()
|
|
})
|
|
router.Use(RequirePermission(mockRoleService, "tracks", "create"))
|
|
router.POST("/test", func(c *gin.Context) {
|
|
handlerCalled = true
|
|
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/test", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
assert.False(t, handlerCalled, "Handler should not be called")
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response, "error")
|
|
// P0: Nouveau format AppError
|
|
errorObj, ok := response["error"].(map[string]interface{})
|
|
require.True(t, ok, "Error should be a map")
|
|
assert.Equal(t, "invalid user id type", errorObj["message"])
|
|
|
|
mockRoleService.AssertNotCalled(t, "HasPermission")
|
|
}
|
|
|
|
func TestRequireRole_WithInvalidUserIDType(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
mockRoleService := new(MockRoleService)
|
|
handlerCalled := false
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
c.Set("user_id", "invalid") // Invalid type
|
|
c.Next()
|
|
})
|
|
router.Use(RequireRole(mockRoleService, "admin"))
|
|
router.GET("/test", func(c *gin.Context) {
|
|
handlerCalled = true
|
|
c.JSON(http.StatusOK, gin.H{"message": "success"})
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/test", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code)
|
|
assert.False(t, handlerCalled, "Handler should not be called")
|
|
|
|
var response map[string]interface{}
|
|
err := json.Unmarshal(w.Body.Bytes(), &response)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, response, "error")
|
|
// P0: Nouveau format AppError
|
|
errorObj, ok := response["error"].(map[string]interface{})
|
|
require.True(t, ok, "Error should be a map")
|
|
assert.Equal(t, "invalid user id type", errorObj["message"])
|
|
|
|
mockRoleService.AssertNotCalled(t, "HasRole")
|
|
}
|