653 lines
20 KiB
Go
653 lines
20 KiB
Go
//go:build security
|
|
// +build security
|
|
|
|
package security
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap/zaptest"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
|
|
"veza-backend-api/internal/core/track"
|
|
"veza-backend-api/internal/database"
|
|
"veza-backend-api/internal/handlers"
|
|
"veza-backend-api/internal/middleware"
|
|
"veza-backend-api/internal/models"
|
|
"veza-backend-api/internal/repositories"
|
|
"veza-backend-api/internal/services"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
// setupAuthorizationTestRouter crée un router de test avec authentification complète
|
|
func setupAuthorizationTestRouter(t *testing.T) (*gin.Engine, *gorm.DB, *services.JWTService, map[uuid.UUID]*models.User, func()) {
|
|
gin.SetMode(gin.TestMode)
|
|
logger := zaptest.NewLogger(t)
|
|
|
|
// Setup in-memory SQLite database
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
|
require.NoError(t, err)
|
|
db.Exec("PRAGMA foreign_keys = ON")
|
|
|
|
// Auto-migrate models
|
|
err = db.AutoMigrate(
|
|
&models.User{},
|
|
&models.Track{},
|
|
&models.Playlist{},
|
|
&models.PlaylistTrack{},
|
|
&models.RefreshToken{},
|
|
&models.Session{},
|
|
&models.Role{},
|
|
&models.UserRole{},
|
|
&models.Permission{},
|
|
&models.RolePermission{},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
dbWrapper := &database.Database{GormDB: db}
|
|
|
|
// Create test users with different roles
|
|
users := make(map[uuid.UUID]*models.User)
|
|
|
|
// Regular user
|
|
regularUserID := uuid.New()
|
|
regularUser := &models.User{
|
|
ID: regularUserID,
|
|
Email: "user@example.com",
|
|
Username: "regularuser",
|
|
PasswordHash: "$2a$10$abcdefghijklmnopqrstuvwxyz1234567890",
|
|
IsVerified: true,
|
|
TokenVersion: 1,
|
|
}
|
|
err = db.Create(regularUser).Error
|
|
require.NoError(t, err)
|
|
users[regularUserID] = regularUser
|
|
|
|
// Admin user
|
|
adminUserID := uuid.New()
|
|
adminUser := &models.User{
|
|
ID: adminUserID,
|
|
Email: "admin@example.com",
|
|
Username: "adminuser",
|
|
PasswordHash: "$2a$10$abcdefghijklmnopqrstuvwxyz1234567890",
|
|
IsVerified: true,
|
|
TokenVersion: 1,
|
|
}
|
|
err = db.Create(adminUser).Error
|
|
require.NoError(t, err)
|
|
users[adminUserID] = adminUser
|
|
|
|
// Creator user
|
|
creatorUserID := uuid.New()
|
|
creatorUser := &models.User{
|
|
ID: creatorUserID,
|
|
Email: "creator@example.com",
|
|
Username: "creatoruser",
|
|
PasswordHash: "$2a$10$abcdefghijklmnopqrstuvwxyz1234567890",
|
|
IsVerified: true,
|
|
TokenVersion: 1,
|
|
}
|
|
err = db.Create(creatorUser).Error
|
|
require.NoError(t, err)
|
|
users[creatorUserID] = creatorUser
|
|
|
|
// Setup roles and permissions
|
|
adminRole := &models.Role{Name: "admin", Description: "Administrator"}
|
|
creatorRole := &models.Role{Name: "creator", Description: "Content Creator"}
|
|
premiumRole := &models.Role{Name: "premium", Description: "Premium User"}
|
|
err = db.Create(adminRole).Error
|
|
require.NoError(t, err)
|
|
err = db.Create(creatorRole).Error
|
|
require.NoError(t, err)
|
|
err = db.Create(premiumRole).Error
|
|
require.NoError(t, err)
|
|
|
|
// Assign roles
|
|
adminUserRole := &models.UserRole{UserID: adminUserID, RoleID: adminRole.ID}
|
|
creatorUserRole := &models.UserRole{UserID: creatorUserID, RoleID: creatorRole.ID}
|
|
err = db.Create(adminUserRole).Error
|
|
require.NoError(t, err)
|
|
err = db.Create(creatorUserRole).Error
|
|
require.NoError(t, err)
|
|
|
|
// Setup services
|
|
jwtService, err := services.NewJWTService("", "", "test-secret-key-must-be-32-chars-long", "test-issuer", "test-audience")
|
|
require.NoError(t, err)
|
|
|
|
sessionService := services.NewSessionService(dbWrapper, logger)
|
|
permissionService := services.NewPermissionService(db)
|
|
auditService := services.NewAuditService(dbWrapper, logger)
|
|
|
|
// Create auth middleware
|
|
authMiddleware := middleware.NewAuthMiddleware(
|
|
sessionService,
|
|
auditService,
|
|
permissionService,
|
|
jwtService,
|
|
services.NewUserServiceWithDB(repositories.NewGormUserRepository(db), db),
|
|
nil,
|
|
nil, // TokenBlacklist
|
|
logger,
|
|
)
|
|
|
|
uploadDir := t.TempDir()
|
|
trackService := track.NewTrackService(db, logger, uploadDir)
|
|
trackUploadService := services.NewTrackUploadService(db, logger)
|
|
chunkService := services.NewTrackChunkService(t.TempDir(), nil, logger)
|
|
likeService := services.NewTrackLikeService(db, logger)
|
|
streamService := services.NewStreamService("http://localhost:8082", logger)
|
|
trackHandler := track.NewTrackHandler(trackService, trackUploadService, chunkService, likeService, streamService)
|
|
|
|
playlistRepo := repositories.NewPlaylistRepository(db)
|
|
playlistTrackRepo := repositories.NewPlaylistTrackRepository(db)
|
|
playlistCollaboratorRepo := repositories.NewPlaylistCollaboratorRepository(db)
|
|
userRepo := repositories.NewGormUserRepository(db)
|
|
playlistService := services.NewPlaylistService(playlistRepo, playlistTrackRepo, playlistCollaboratorRepo, userRepo, logger)
|
|
playlistHandler := handlers.NewPlaylistHandler(playlistService, db, logger)
|
|
|
|
userService := services.NewUserServiceWithDB(userRepo, db)
|
|
profileHandler := handlers.NewProfileHandler(userService, logger)
|
|
|
|
// Create router
|
|
router := gin.New()
|
|
|
|
// Public routes
|
|
router.GET("/health", func(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
|
})
|
|
|
|
// Protected routes
|
|
protected := router.Group("/api/v1")
|
|
protected.Use(authMiddleware.RequireAuth())
|
|
|
|
// Admin routes
|
|
admin := protected.Group("/admin")
|
|
admin.Use(authMiddleware.RequireAdmin())
|
|
{
|
|
admin.GET("/users", func(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"message": "admin only"})
|
|
})
|
|
}
|
|
|
|
// Content creator routes
|
|
creator := protected.Group("/tracks")
|
|
creator.POST("", authMiddleware.RequireContentCreatorRole(), trackHandler.UploadTrack)
|
|
|
|
// Track routes with ownership
|
|
tracks := protected.Group("/tracks")
|
|
{
|
|
tracks.GET("", trackHandler.ListTracks)
|
|
tracks.GET("/:id", trackHandler.GetTrack)
|
|
|
|
// Ownership resolver for tracks
|
|
trackOwnerResolver := func(c *gin.Context) (uuid.UUID, error) {
|
|
trackIDStr := c.Param("id")
|
|
trackID, err := uuid.Parse(trackIDStr)
|
|
if err != nil {
|
|
return uuid.Nil, err
|
|
}
|
|
track, err := trackService.GetTrackByID(c.Request.Context(), trackID)
|
|
if err != nil {
|
|
return uuid.Nil, err
|
|
}
|
|
return track.UserID, nil
|
|
}
|
|
tracks.PUT("/:id", authMiddleware.RequireOwnershipOrAdmin("track", trackOwnerResolver), trackHandler.UpdateTrack)
|
|
tracks.DELETE("/:id", authMiddleware.RequireOwnershipOrAdmin("track", trackOwnerResolver), trackHandler.DeleteTrack)
|
|
}
|
|
|
|
// Playlist routes
|
|
playlists := protected.Group("/playlists")
|
|
{
|
|
playlists.GET("", playlistHandler.GetPlaylists)
|
|
playlists.POST("", playlistHandler.CreatePlaylist)
|
|
playlists.GET("/:id", playlistHandler.GetPlaylist)
|
|
}
|
|
|
|
// User routes
|
|
usersGroup := protected.Group("/users")
|
|
{
|
|
usersGroup.GET("", profileHandler.ListUsers)
|
|
usersGroup.GET("/:id", profileHandler.GetProfile)
|
|
|
|
// Ownership resolver for users
|
|
userOwnerResolver := func(c *gin.Context) (uuid.UUID, error) {
|
|
userIDStr := c.Param("id")
|
|
userID, err := uuid.Parse(userIDStr)
|
|
if err != nil {
|
|
return uuid.Nil, err
|
|
}
|
|
return userID, nil
|
|
}
|
|
usersGroup.PUT("/:id", authMiddleware.RequireOwnershipOrAdmin("user", userOwnerResolver), profileHandler.UpdateProfile)
|
|
}
|
|
|
|
cleanup := func() {
|
|
// Cleanup handled by t.TempDir()
|
|
}
|
|
|
|
return router, db, jwtService, users, cleanup
|
|
}
|
|
|
|
// generateToken génère un token JWT pour un utilisateur
|
|
func generateToken(jwtService *services.JWTService, user *models.User) (string, error) {
|
|
return jwtService.GenerateAccessToken(user)
|
|
}
|
|
|
|
// TestAuthorization_NoToken teste que les requêtes sans token sont rejetées
|
|
func TestAuthorization_NoToken(t *testing.T) {
|
|
router, _, _, _, cleanup := setupAuthorizationTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Test various protected endpoints without token
|
|
endpoints := []struct {
|
|
method string
|
|
path string
|
|
}{
|
|
{"GET", "/api/v1/tracks"},
|
|
{"GET", "/api/v1/tracks/123e4567-e89b-12d3-a456-426614174000"},
|
|
{"POST", "/api/v1/tracks"},
|
|
{"PUT", "/api/v1/tracks/123e4567-e89b-12d3-a456-426614174000"},
|
|
{"DELETE", "/api/v1/tracks/123e4567-e89b-12d3-a456-426614174000"},
|
|
{"GET", "/api/v1/admin/users"},
|
|
{"GET", "/api/v1/users"},
|
|
{"PUT", "/api/v1/users/123e4567-e89b-12d3-a456-426614174000"},
|
|
}
|
|
|
|
for _, endpoint := range endpoints {
|
|
t.Run(fmt.Sprintf("%s %s", endpoint.method, endpoint.path), func(t *testing.T) {
|
|
req := httptest.NewRequest(endpoint.method, endpoint.path, nil)
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code,
|
|
"Request without token should be rejected with 401")
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestAuthorization_InvalidToken teste que les requêtes avec token invalide sont rejetées
|
|
func TestAuthorization_InvalidToken(t *testing.T) {
|
|
router, _, _, _, cleanup := setupAuthorizationTestRouter(t)
|
|
defer cleanup()
|
|
|
|
invalidTokens := []string{
|
|
"invalid-token",
|
|
"Bearer invalid-token",
|
|
"not-a-bearer-token",
|
|
"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.invalid",
|
|
"",
|
|
}
|
|
|
|
for _, token := range invalidTokens {
|
|
t.Run(fmt.Sprintf("Invalid token: %s", token), func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/tracks", nil)
|
|
if token != "" {
|
|
req.Header.Set("Authorization", token)
|
|
}
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code,
|
|
"Request with invalid token should be rejected with 401")
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestAuthorization_ExpiredToken teste que les requêtes avec token expiré sont rejetées
|
|
func TestAuthorization_ExpiredToken(t *testing.T) {
|
|
router, _, _, users, cleanup := setupAuthorizationTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Create expired token manually
|
|
userID := uuid.New()
|
|
for uid := range users {
|
|
userID = uid
|
|
break
|
|
}
|
|
user := users[userID]
|
|
|
|
// Create expired claims manually
|
|
claims := models.CustomClaims{
|
|
UserID: user.ID,
|
|
Email: user.Email,
|
|
Username: user.Username,
|
|
Role: user.Role,
|
|
TokenVersion: user.TokenVersion,
|
|
TokenType: "access",
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(-1 * time.Hour)), // Expired
|
|
IssuedAt: jwt.NewNumericDate(time.Now().Add(-2 * time.Hour)),
|
|
Issuer: "test-issuer",
|
|
Audience: jwt.ClaimStrings{"test-audience"},
|
|
ID: uuid.NewString(),
|
|
},
|
|
}
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
expiredToken, err := token.SignedString([]byte("test-secret-key-must-be-32-chars-long"))
|
|
require.NoError(t, err)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/tracks", nil)
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", expiredToken))
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code,
|
|
"Request with expired token should be rejected with 401")
|
|
}
|
|
|
|
// TestAuthorization_RegularUser_AdminEndpoint teste qu'un utilisateur régulier ne peut pas accéder aux endpoints admin
|
|
func TestAuthorization_RegularUser_AdminEndpoint(t *testing.T) {
|
|
router, _, jwtService, users, cleanup := setupAuthorizationTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Find regular user (not admin)
|
|
var regularUserID uuid.UUID
|
|
for uid, user := range users {
|
|
if user.Email == "user@example.com" {
|
|
regularUserID = uid
|
|
break
|
|
}
|
|
}
|
|
|
|
regularUser := users[regularUserID]
|
|
token, err := generateToken(jwtService, regularUser)
|
|
require.NoError(t, err)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/users", nil)
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusForbidden, w.Code,
|
|
"Regular user should not be able to access admin endpoints")
|
|
}
|
|
|
|
// TestAuthorization_RegularUser_CreatorEndpoint teste qu'un utilisateur régulier ne peut pas créer de contenu
|
|
func TestAuthorization_RegularUser_CreatorEndpoint(t *testing.T) {
|
|
router, _, jwtService, users, cleanup := setupAuthorizationTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Find regular user
|
|
var regularUserID uuid.UUID
|
|
for uid, user := range users {
|
|
if user.Email == "user@example.com" {
|
|
regularUserID = uid
|
|
break
|
|
}
|
|
}
|
|
|
|
regularUser := users[regularUserID]
|
|
token, err := generateToken(jwtService, regularUser)
|
|
require.NoError(t, err)
|
|
|
|
// Try to upload a track (requires creator role)
|
|
payload := map[string]interface{}{
|
|
"title": "Test Track",
|
|
"artist": "Test Artist",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/tracks", bytes.NewBuffer(body))
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusForbidden, w.Code,
|
|
"Regular user should not be able to create content")
|
|
}
|
|
|
|
// TestAuthorization_AdminUser_AdminEndpoint teste qu'un admin peut accéder aux endpoints admin
|
|
func TestAuthorization_AdminUser_AdminEndpoint(t *testing.T) {
|
|
router, _, jwtService, users, cleanup := setupAuthorizationTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Find admin user
|
|
var adminUserID uuid.UUID
|
|
for uid, user := range users {
|
|
if user.Email == "admin@example.com" {
|
|
adminUserID = uid
|
|
break
|
|
}
|
|
}
|
|
|
|
adminUser := users[adminUserID]
|
|
token, err := generateToken(jwtService, adminUser)
|
|
require.NoError(t, err)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/users", nil)
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code,
|
|
"Admin user should be able to access admin endpoints")
|
|
}
|
|
|
|
// TestAuthorization_CreatorUser_CreatorEndpoint teste qu'un creator peut créer du contenu
|
|
func TestAuthorization_CreatorUser_CreatorEndpoint(t *testing.T) {
|
|
router, _, jwtService, users, cleanup := setupAuthorizationTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Find creator user
|
|
var creatorUserID uuid.UUID
|
|
for uid, user := range users {
|
|
if user.Email == "creator@example.com" {
|
|
creatorUserID = uid
|
|
break
|
|
}
|
|
}
|
|
|
|
creatorUser := users[creatorUserID]
|
|
token, err := generateToken(jwtService, creatorUser)
|
|
require.NoError(t, err)
|
|
|
|
// Try to upload a track (requires creator role)
|
|
payload := map[string]interface{}{
|
|
"title": "Test Track",
|
|
"artist": "Test Artist",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/tracks", bytes.NewBuffer(body))
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
// Should not be forbidden (may be 400/422 for validation, but not 403)
|
|
assert.NotEqual(t, http.StatusForbidden, w.Code,
|
|
"Creator user should be able to create content")
|
|
}
|
|
|
|
// TestAuthorization_Ownership_OtherUserResource teste qu'un utilisateur ne peut pas modifier les ressources d'un autre utilisateur
|
|
func TestAuthorization_Ownership_OtherUserResource(t *testing.T) {
|
|
router, db, jwtService, users, cleanup := setupAuthorizationTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Create a track owned by creator user
|
|
var creatorUserID uuid.UUID
|
|
for uid, user := range users {
|
|
if user.Email == "creator@example.com" {
|
|
creatorUserID = uid
|
|
break
|
|
}
|
|
}
|
|
|
|
trackID := uuid.New()
|
|
track := &models.Track{
|
|
ID: trackID,
|
|
UserID: creatorUserID,
|
|
Title: "Creator's Track",
|
|
IsPublic: true,
|
|
}
|
|
err := db.Create(track).Error
|
|
require.NoError(t, err)
|
|
|
|
// Try to update as regular user
|
|
var regularUserID uuid.UUID
|
|
for uid, user := range users {
|
|
if user.Email == "user@example.com" {
|
|
regularUserID = uid
|
|
break
|
|
}
|
|
}
|
|
|
|
regularUser := users[regularUserID]
|
|
token, err := generateToken(jwtService, regularUser)
|
|
require.NoError(t, err)
|
|
|
|
payload := map[string]interface{}{
|
|
"title": "Hacked Title",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/api/v1/tracks/%s", trackID), bytes.NewBuffer(body))
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusForbidden, w.Code,
|
|
"User should not be able to modify another user's resource")
|
|
}
|
|
|
|
// TestAuthorization_Ownership_OwnResource teste qu'un utilisateur peut modifier ses propres ressources
|
|
func TestAuthorization_Ownership_OwnResource(t *testing.T) {
|
|
router, db, jwtService, users, cleanup := setupAuthorizationTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Create a track owned by regular user
|
|
var regularUserID uuid.UUID
|
|
for uid, user := range users {
|
|
if user.Email == "user@example.com" {
|
|
regularUserID = uid
|
|
break
|
|
}
|
|
}
|
|
|
|
trackID := uuid.New()
|
|
track := &models.Track{
|
|
ID: trackID,
|
|
UserID: regularUserID,
|
|
Title: "My Track",
|
|
IsPublic: true,
|
|
}
|
|
err := db.Create(track).Error
|
|
require.NoError(t, err)
|
|
|
|
regularUser := users[regularUserID]
|
|
token, err := generateToken(jwtService, regularUser)
|
|
require.NoError(t, err)
|
|
|
|
payload := map[string]interface{}{
|
|
"title": "Updated Title",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/api/v1/tracks/%s", trackID), bytes.NewBuffer(body))
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
// Should not be forbidden (may be 400/422 for validation, but not 403)
|
|
assert.NotEqual(t, http.StatusForbidden, w.Code,
|
|
"User should be able to modify their own resource")
|
|
}
|
|
|
|
// TestAuthorization_Admin_OwnershipOverride teste qu'un admin peut modifier n'importe quelle ressource
|
|
func TestAuthorization_Admin_OwnershipOverride(t *testing.T) {
|
|
router, db, jwtService, users, cleanup := setupAuthorizationTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Create a track owned by regular user
|
|
var regularUserID uuid.UUID
|
|
for uid, user := range users {
|
|
if user.Email == "user@example.com" {
|
|
regularUserID = uid
|
|
break
|
|
}
|
|
}
|
|
|
|
trackID := uuid.New()
|
|
track := &models.Track{
|
|
ID: trackID,
|
|
UserID: regularUserID,
|
|
Title: "User's Track",
|
|
IsPublic: true,
|
|
}
|
|
err := db.Create(track).Error
|
|
require.NoError(t, err)
|
|
|
|
// Try to update as admin
|
|
var adminUserID uuid.UUID
|
|
for uid, user := range users {
|
|
if user.Email == "admin@example.com" {
|
|
adminUserID = uid
|
|
break
|
|
}
|
|
}
|
|
|
|
adminUser := users[adminUserID]
|
|
token, err := generateToken(jwtService, adminUser)
|
|
require.NoError(t, err)
|
|
|
|
payload := map[string]interface{}{
|
|
"title": "Admin Updated Title",
|
|
}
|
|
body, _ := json.Marshal(payload)
|
|
|
|
req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/api/v1/tracks/%s", trackID), bytes.NewBuffer(body))
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
// Should not be forbidden (admin can override ownership)
|
|
assert.NotEqual(t, http.StatusForbidden, w.Code,
|
|
"Admin should be able to modify any resource")
|
|
}
|
|
|
|
// TestAuthorization_TokenVersionMismatch teste que les tokens avec version incorrecte sont rejetés
|
|
func TestAuthorization_TokenVersionMismatch(t *testing.T) {
|
|
router, db, jwtService, users, cleanup := setupAuthorizationTestRouter(t)
|
|
defer cleanup()
|
|
|
|
// Get a user
|
|
var userID uuid.UUID
|
|
for uid := range users {
|
|
userID = uid
|
|
break
|
|
}
|
|
|
|
// Get user and generate token with old version
|
|
user := users[userID]
|
|
token, err := generateToken(jwtService, user)
|
|
require.NoError(t, err)
|
|
|
|
// Update user's token version (simulating logout or password change)
|
|
err = db.Model(&models.User{}).Where("id = ?", userID).Update("token_version", 2).Error
|
|
require.NoError(t, err)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/tracks", nil)
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, w.Code,
|
|
"Token with mismatched version should be rejected")
|
|
}
|