Add delete account endpoint and file encryption at rest
Delete Account (Plan #2): - DELETE /api/auth/account/ with password or "DELETE" confirmation - Cascade delete across 15+ tables in correct FK order - Auth provider detection (email/apple/google) for /auth/me/ - File cleanup after account deletion - Handler + repository tests (12 tests) Encryption at Rest (Plan #3): - AES-256-GCM envelope encryption (per-file DEK wrapped by KEK) - Encrypt on upload, auto-decrypt on serve via StorageService.ReadFile() - MediaHandler serves decrypted files via c.Blob() - TaskService email image loading uses ReadFile() - cmd/migrate-encrypt CLI tool with --dry-run for existing files - Encryption service + storage service tests (18 tests)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -137,10 +138,11 @@ type SecurityConfig struct {
|
||||
|
||||
// StorageConfig holds file storage settings
|
||||
type StorageConfig struct {
|
||||
UploadDir string // Directory to store uploaded files
|
||||
BaseURL string // Public URL prefix for serving files (e.g., "/uploads")
|
||||
MaxFileSize int64 // Max file size in bytes (default 10MB)
|
||||
AllowedTypes string // Comma-separated MIME types
|
||||
UploadDir string // Directory to store uploaded files
|
||||
BaseURL string // Public URL prefix for serving files (e.g., "/uploads")
|
||||
MaxFileSize int64 // Max file size in bytes (default 10MB)
|
||||
AllowedTypes string // Comma-separated MIME types
|
||||
EncryptionKey string // 64-char hex key for file encryption at rest (optional)
|
||||
}
|
||||
|
||||
// FeatureFlags holds kill switches for major subsystems.
|
||||
@@ -262,10 +264,11 @@ func Load() (*Config, error) {
|
||||
MaxPasswordResetRate: 3,
|
||||
},
|
||||
Storage: StorageConfig{
|
||||
UploadDir: viper.GetString("STORAGE_UPLOAD_DIR"),
|
||||
BaseURL: viper.GetString("STORAGE_BASE_URL"),
|
||||
MaxFileSize: viper.GetInt64("STORAGE_MAX_FILE_SIZE"),
|
||||
AllowedTypes: viper.GetString("STORAGE_ALLOWED_TYPES"),
|
||||
UploadDir: viper.GetString("STORAGE_UPLOAD_DIR"),
|
||||
BaseURL: viper.GetString("STORAGE_BASE_URL"),
|
||||
MaxFileSize: viper.GetInt64("STORAGE_MAX_FILE_SIZE"),
|
||||
AllowedTypes: viper.GetString("STORAGE_ALLOWED_TYPES"),
|
||||
EncryptionKey: viper.GetString("STORAGE_ENCRYPTION_KEY"),
|
||||
},
|
||||
AppleAuth: AppleAuthConfig{
|
||||
ClientID: viper.GetString("APPLE_CLIENT_ID"),
|
||||
@@ -414,6 +417,16 @@ func validate(cfg *Config) error {
|
||||
// Database password might come from DATABASE_URL, don't require it separately
|
||||
// The actual connection will fail if credentials are wrong
|
||||
|
||||
// Validate STORAGE_ENCRYPTION_KEY if set: must be exactly 64 hex characters
|
||||
if cfg.Storage.EncryptionKey != "" {
|
||||
if len(cfg.Storage.EncryptionKey) != 64 {
|
||||
return fmt.Errorf("STORAGE_ENCRYPTION_KEY must be exactly 64 hex characters (got %d)", len(cfg.Storage.EncryptionKey))
|
||||
}
|
||||
if _, err := hex.DecodeString(cfg.Storage.EncryptionKey); err != nil {
|
||||
return fmt.Errorf("STORAGE_ENCRYPTION_KEY contains invalid hex: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user