Initial commit: Open sourcing all of the Maple Open Technologies code.

This commit is contained in:
Bartlomiej Mika 2025-12-02 14:33:08 -05:00
commit 755d54a99d
2010 changed files with 448675 additions and 0 deletions

View file

@ -0,0 +1,188 @@
package file
import (
"fmt"
"strings"
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
)
// File size limits (in bytes)
const (
// MaxFileSizeGeneral is the maximum file size for general folders (500MB)
MaxFileSizeGeneral = 500 * 1024 * 1024
// MaxFileSizeAlbum is the maximum file size for album uploads (100MB)
// Albums are for photos/videos, so we use a more restrictive limit
MaxFileSizeAlbum = 100 * 1024 * 1024
// MaxThumbnailSize is the maximum thumbnail size (10MB)
MaxThumbnailSize = 10 * 1024 * 1024
)
// Allowed content types for albums (photos and videos only)
var AlbumAllowedContentTypes = []string{
// Image formats
"image/jpeg",
"image/jpg",
"image/png",
"image/gif",
"image/webp",
"image/heic",
"image/heif",
"image/bmp",
"image/tiff",
"image/svg+xml",
// Video formats
"video/mp4",
"video/mpeg",
"video/quicktime", // .mov files
"video/x-msvideo", // .avi files
"video/x-matroska", // .mkv files
"video/webm",
"video/3gpp",
"video/x-flv",
}
// FileValidator provides file upload validation based on collection type
type FileValidator struct{}
// NewFileValidator creates a new file validator
func NewFileValidator() *FileValidator {
return &FileValidator{}
}
// ValidateFileUpload validates a file upload based on collection type and file properties
// CWE-434: Unrestricted Upload of File with Dangerous Type
// OWASP A01:2021: Broken Access Control - File upload restrictions
func (v *FileValidator) ValidateFileUpload(
collectionType string,
fileSize int64,
thumbnailSize int64,
contentType string,
) error {
// Validate file size based on collection type
if err := v.validateFileSize(collectionType, fileSize); err != nil {
return err
}
// Validate thumbnail size if provided
if thumbnailSize > 0 {
if err := v.validateThumbnailSize(thumbnailSize); err != nil {
return err
}
}
// Validate content type for albums (photos/videos only)
if collectionType == dom_collection.CollectionTypeAlbum {
if err := v.validateContentType(contentType); err != nil {
return err
}
}
// For folders (non-albums), no content-type restrictions
// Users can upload any file type to regular folders
return nil
}
// validateFileSize checks if the file size is within limits
func (v *FileValidator) validateFileSize(collectionType string, fileSize int64) error {
if fileSize <= 0 {
return fmt.Errorf("file size must be greater than 0")
}
var maxSize int64
var collectionTypeName string
if collectionType == dom_collection.CollectionTypeAlbum {
maxSize = MaxFileSizeAlbum
collectionTypeName = "album"
} else {
maxSize = MaxFileSizeGeneral
collectionTypeName = "folder"
}
if fileSize > maxSize {
return fmt.Errorf(
"file size (%s) exceeds maximum allowed size for %s (%s)",
formatBytes(fileSize),
collectionTypeName,
formatBytes(maxSize),
)
}
return nil
}
// validateThumbnailSize checks if the thumbnail size is within limits
func (v *FileValidator) validateThumbnailSize(thumbnailSize int64) error {
if thumbnailSize <= 0 {
return nil // Thumbnail is optional
}
if thumbnailSize > MaxThumbnailSize {
return fmt.Errorf(
"thumbnail size (%s) exceeds maximum allowed size (%s)",
formatBytes(thumbnailSize),
formatBytes(MaxThumbnailSize),
)
}
return nil
}
// validateContentType checks if the content type is allowed for albums
func (v *FileValidator) validateContentType(contentType string) error {
if contentType == "" {
return fmt.Errorf("content type is required for album uploads")
}
// Normalize content type (lowercase and trim)
normalizedType := strings.ToLower(strings.TrimSpace(contentType))
// Check if content type is in allowed list
for _, allowedType := range AlbumAllowedContentTypes {
if normalizedType == allowedType {
return nil
}
}
return fmt.Errorf(
"content type '%s' is not allowed in albums. Only photos and videos are permitted",
contentType,
)
}
// GetAllowedContentTypes returns the list of allowed content types for albums
func (v *FileValidator) GetAllowedContentTypes() []string {
return AlbumAllowedContentTypes
}
// GetMaxFileSize returns the maximum file size for a collection type
func (v *FileValidator) GetMaxFileSize(collectionType string) int64 {
if collectionType == dom_collection.CollectionTypeAlbum {
return MaxFileSizeAlbum
}
return MaxFileSizeGeneral
}
// GetMaxThumbnailSize returns the maximum thumbnail size
func (v *FileValidator) GetMaxThumbnailSize() int64 {
return MaxThumbnailSize
}
// formatBytes formats byte size into human-readable format
func formatBytes(bytes int64) string {
const unit = 1024
if bytes < unit {
return fmt.Sprintf("%d B", bytes)
}
div, exp := int64(unit), 0
for n := bytes / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}