diff --git a/.env.example b/.env.example index ae18bd5..7d0ad4e 100644 --- a/.env.example +++ b/.env.example @@ -8,9 +8,6 @@ DB_NAME=mailserver # Security - JWT secret must be at least 32 characters JWT_SECRET=your-secret-key-at-least-32-chars -# Admin user (optional - use --reset-admin-password to set password) -ADMIN_USER=admin - # SMTP settings for password reset emails SMTP_HOST=localhost SMTP_PORT=587 diff --git a/backend/.env b/backend/.env index c75d2ef..6be6a33 100644 --- a/backend/.env +++ b/backend/.env @@ -1,11 +1,9 @@ DB_HOST=localhost DB_PORT=3306 DB_USER=mailadmin -DB_PASSWORD=MAILADMIN-PASSWORD-HERE +DB_PASSWORD=your-db-password DB_NAME=mailserver JWT_SECRET=change-this-secret-in-production -ADMIN_USER=admin -ADMIN_PASSWORD=admin123 SMTP_HOST=localhost SMTP_PORT=587 diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 11425c3..47cffb6 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -3,17 +3,15 @@ package main import ( - "crypto/rand" // cryptographically secure random number generator - "flag" // standard library for parsing command-line flags - "fmt" // formatted I/O, used here for printing output - "log" // logging package - "net/http" // HTTP server and client - "os" // OS-level operations like reading command-line args - "strings" // string manipulation utilities + "flag" // standard library for parsing command-line flags + "fmt" // formatted I/O, used here for printing output + "log" // logging package + "net/http" // HTTP server and client + "os" // OS-level operations like reading command-line args + "strings" // string manipulation utilities "github.com/gin-gonic/gin" // web framework "github.com/imc-vibe/backend/internal/api" // HTTP API routing and handlers - "github.com/imc-vibe/backend/internal/auth" // JWT authentication "github.com/imc-vibe/backend/internal/config" // configuration loading "github.com/imc-vibe/backend/internal/db" // database connection and operations ) @@ -26,10 +24,6 @@ func main() { // Flags are optional arguments passed after the program name. // Example: ./imc-vibe --bind=0.0.0.0 --port=8080 - // --reset-admin-password: If set, generate a random password for the admin user and exit. - // Useful for initial setup or recovering from a lost password. - resetAdminPassword := flag.Bool("reset-admin-password", false, "Reset admin password to a random value and exit") - // --bind: The IP address the server should listen on. // 0.0.0.0 means listen on all network interfaces (accessible from other machines). // 127.0.0.1 means listen only on localhost (accessible only from this machine). @@ -81,33 +75,6 @@ func main() { log.Printf("Warning: Could not initialize schema: %v", err) // Non-fatal = continue } - // Handle --reset-admin-password flag. - // When this flag is set, we generate a random password, update the database, and exit. - // We don't start the web server in this case. - if *resetAdminPassword { - // Generate a cryptographically secure random password. - // The password will be 16 characters long, containing letters, numbers, and symbols. - password := generateRandomPassword(16) - fmt.Printf("Generated password: %s\n", password) - - // Hash the password before storing it. - // We never store plain-text passwords, only their hashes. - // This uses bcrypt, which is a slow hashing algorithm designed to resist brute-force attacks. - hash, err := auth.HashPassword(password) - if err != nil { - log.Fatalf("Failed to hash password: %v", err) - } - - // Store or update the admin user in the database. - // EnsureAdminUser creates the user if it doesn't exist, or updates the password if it does. - if err := database.EnsureAdminUser("admin", hash); err != nil { - log.Fatalf("Failed to reset admin password: %v", err) - } - - fmt.Println("Admin password reset successfully.") - return // Exit the program - } - // Start the web server. // From here on, we set up the HTTP router and handlers. @@ -195,33 +162,6 @@ func serveGinStaticFile(c *gin.Context, fs http.FileSystem, path string) { http.ServeContent(c.Writer, c.Request, fi.Name(), fi.ModTime(), file) } -// generateRandomPassword creates a cryptographically secure random password. -// length: how many characters the password should have. -// Returns: a string containing random characters from a safe charset. -func generateRandomPassword(length int) string { - // Characters that are safe to use in passwords. - // We avoid ambiguous characters like 0/O and 1/l/I. - const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*" - - // Create a byte slice to hold random bytes. - result := make([]byte, length) - - // Use crypto/rand for cryptographically secure randomness. - // This is better than math/rand for security-sensitive applications. - if _, err := rand.Read(result); err != nil { - log.Fatalf("Failed to generate random password: %v", err) - } - - // Convert random bytes to characters from our charset. - // We use modulo to map bytes to charset indices. - // This isn't perfectly uniform distribution, but is good enough for passwords. - for i := range result { - result[i] = charset[int(result[i])%len(charset)] - } - - return string(result) // Convert bytes to string -} - // mimeType returns the MIME type for a file based on its extension. // MIME types tell the browser what kind of file it is. // This is important for browsers to correctly interpret files. diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 7720685..d02ab0f 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -28,10 +28,6 @@ type Config struct { JWTSecret string // Secret key for signing JWT tokens (keep this secret!) TrustedProxies []string // IPs of trusted reverse proxies (for X-Forwarded-For) - // Admin user configuration (used when --reset-admin-password is not used) - AdminUser string // Username for the admin account - AdminPassword string // Password for the admin account (only used during startup) - // Mail server paths and settings MailDataDir string // Directory where mail is stored (e.g., /var/vmail) PostqueuePath string // Path to postqueue command (for mail queue operations) @@ -94,10 +90,6 @@ func Load() *Config { // Default JWT secret is insecure - must be changed in production! JWTSecret: getEnv("JWT_SECRET", "change-this-secret-in-production"), - // Admin user (optional - used during startup if set) - AdminUser: getEnv("ADMIN_USER", ""), - AdminPassword: getEnv("ADMIN_PASSWORD", ""), - // Mail server paths MailDataDir: getEnv("MAIL_DATA_DIR", "/var/vmail"), PostqueuePath: getEnv("POSTQUEUE_PATH", "/usr/sbin/postqueue"), diff --git a/backend/internal/db/imc_users.go b/backend/internal/db/imc_users.go index 98d7c22..1c703f6 100644 --- a/backend/internal/db/imc_users.go +++ b/backend/internal/db/imc_users.go @@ -68,23 +68,6 @@ func (d *DB) DeleteImcUser(id uint) error { return d.Delete(&ImcUser{}, id).Error // Delete by primary key } -// EnsureAdminUser creates the admin user if it doesn't exist, or updates the password if it does. -// This is used during --reset-admin-password and startup. -// username: always "admin" in our case. -// passwordHash: the bcrypt hash of the password. -func (d *DB) EnsureAdminUser(username, passwordHash string) error { - var user ImcUser - // Try to find an existing admin user with this username. - err := d.Where("username = ? AND role = ?", username, "admin").First(&user).Error - if err == nil { - // User exists - update their password. - return d.Model(&ImcUser{}).Where("id = ?", user.ID).Update("password_hash", passwordHash).Error - } - // User doesn't exist - create them. - _, err = d.CreateImcUser(username, passwordHash, "admin") - return err -} - // ============================================================================= // Domain Access Control // These functions manage which domains users can access.