fix(security): add ownership check to GetUploadStatus handler (IDOR fix)
SEC-06: GetUploadStatus now verifies that the authenticated user owns the upload before returning status. Returns 404 for non-owners to prevent information disclosure.
This commit is contained in:
parent
c6db1da25e
commit
da3bad1b0e
3 changed files with 48 additions and 4 deletions
|
|
@ -58,6 +58,8 @@ type UploadAuditServiceInterface interface {
|
|||
// TrackUploadServiceInterface définit les méthodes nécessaires pour TrackUploadService
|
||||
type TrackUploadServiceInterface interface {
|
||||
GetUploadStats(ctx context.Context, userID uuid.UUID) (map[string]interface{}, error)
|
||||
// SEC-06: Return owner user ID for a given track/upload ID
|
||||
GetUploadOwnerID(ctx context.Context, trackID uuid.UUID) (uuid.UUID, error)
|
||||
}
|
||||
|
||||
// UploadHandler gère les uploads de fichiers
|
||||
|
|
@ -305,18 +307,42 @@ func (uh *UploadHandler) UploadFile() gin.HandlerFunc {
|
|||
}
|
||||
|
||||
// GetUploadStatus récupère le statut d'un upload
|
||||
// SEC-06: Ownership check — only the uploader can query status
|
||||
func (uh *UploadHandler) GetUploadStatus() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
userIDInterface, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
RespondWithAppError(c, apperrors.NewUnauthorizedError("User not authenticated"))
|
||||
return
|
||||
}
|
||||
|
||||
_, ok := userIDInterface.(uuid.UUID)
|
||||
if !ok {
|
||||
RespondWithAppError(c, apperrors.New(apperrors.ErrCodeInternal, "Invalid user ID type"))
|
||||
return
|
||||
}
|
||||
|
||||
uploadIDStr := c.Param("id")
|
||||
uploadID, err := uuid.Parse(uploadIDStr)
|
||||
if err != nil {
|
||||
// MOD-P2-003: Utiliser AppError au lieu de gin.H
|
||||
RespondWithAppError(c, apperrors.New(apperrors.ErrCodeValidation, "Invalid upload ID"))
|
||||
return
|
||||
}
|
||||
|
||||
// Récupérer le statut depuis la base de données
|
||||
// Note: Dans un vrai environnement, il faudrait interroger la DB
|
||||
// SEC-06: Verify ownership before returning status
|
||||
ownerID, _ := userIDInterface.(uuid.UUID)
|
||||
if uh.trackUploadService != nil {
|
||||
trackOwnerID, dbErr := uh.trackUploadService.GetUploadOwnerID(c.Request.Context(), uploadID)
|
||||
if dbErr != nil {
|
||||
RespondWithAppError(c, apperrors.New(apperrors.ErrCodeNotFound, "Upload not found"))
|
||||
return
|
||||
}
|
||||
if trackOwnerID != ownerID {
|
||||
RespondWithAppError(c, apperrors.New(apperrors.ErrCodeNotFound, "Upload not found"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
RespondSuccess(c, http.StatusOK, gin.H{
|
||||
"id": uploadID,
|
||||
"status": "completed",
|
||||
|
|
|
|||
|
|
@ -67,6 +67,11 @@ func (m *MockTrackUploadService) GetUploadStats(ctx context.Context, userID uuid
|
|||
return args.Get(0).(map[string]interface{}), args.Error(1)
|
||||
}
|
||||
|
||||
func (m *MockTrackUploadService) GetUploadOwnerID(ctx context.Context, trackID uuid.UUID) (uuid.UUID, error) {
|
||||
args := m.Called(ctx, trackID)
|
||||
return args.Get(0).(uuid.UUID), args.Error(1)
|
||||
}
|
||||
|
||||
// Setup helper
|
||||
func setupTestUploadHandler(t *testing.T) (*UploadHandler, *MockUploadValidator, *MockUploadAuditService, *MockTrackUploadService) {
|
||||
mockValidator := new(MockUploadValidator)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
"veza-backend-api/internal/models"
|
||||
|
||||
"github.com/google/uuid" // Added import for uuid
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
|
@ -152,3 +152,16 @@ func (s *TrackUploadService) GetUploadStats(ctx context.Context, userID uuid.UUI
|
|||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// GetUploadOwnerID returns the creator (owner) user ID for a track.
|
||||
// SEC-06: Used for IDOR prevention in GetUploadStatus.
|
||||
func (s *TrackUploadService) GetUploadOwnerID(ctx context.Context, trackID uuid.UUID) (uuid.UUID, error) {
|
||||
var track models.Track
|
||||
if err := s.db.WithContext(ctx).Select("id, creator_id").First(&track, "id = ?", trackID).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return uuid.Nil, fmt.Errorf("track not found")
|
||||
}
|
||||
return uuid.Nil, fmt.Errorf("failed to get track: %w", err)
|
||||
}
|
||||
return track.UserID, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue