From 53261dec0cf6ee714cb2ec0592356679ef269e03 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Tue, 24 Mar 2026 21:57:32 +0100 Subject: [PATCH] Add backend validation for aliases - Validate source email format (RFC 5321 local-part) - Ensure source is in the correct domain - Validate destination (single or comma-separated emails) - Check for duplicate aliases - Add GetAliasBySource database method --- backend/internal/api/handlers/aliases.go | 37 ++++++++++++++++++++++++ backend/internal/db/aliases.go | 8 +++++ 2 files changed, 45 insertions(+) diff --git a/backend/internal/api/handlers/aliases.go b/backend/internal/api/handlers/aliases.go index 879dfa6..9776926 100644 --- a/backend/internal/api/handlers/aliases.go +++ b/backend/internal/api/handlers/aliases.go @@ -3,6 +3,7 @@ package handlers import ( "net/http" "strconv" + "strings" "github.com/gin-gonic/gin" "github.com/imc-vibe/backend/internal/db" @@ -86,6 +87,42 @@ func (h *AliasHandler) Create(c *gin.Context) { return } + // Validate source email format + if err := validateEmailLocalPart(req.Source); err != nil { + Error(c, http.StatusBadRequest, "source "+err.Error()) + return + } + + // Ensure source is in the correct domain + if !strings.HasSuffix(req.Source, "@"+domainName) { + Error(c, http.StatusBadRequest, "source must be in domain "+domainName) + return + } + + // Validate destination - can be single email or comma-separated list + destinations := strings.Split(req.Destination, ",") + for _, dest := range destinations { + dest = strings.TrimSpace(dest) + if dest == "" { + Error(c, http.StatusBadRequest, "destination cannot be empty") + return + } + // Destination doesn't need @domain validation since it can be external + if strings.Contains(dest, "@") { + if err := validateEmailLocalPart(dest); err != nil { + Error(c, http.StatusBadRequest, "destination "+err.Error()) + return + } + } + } + + // Check for duplicate alias + existing, _ := h.db.GetAliasBySource(req.Source) + if existing != nil { + Error(c, http.StatusConflict, "alias already exists") + return + } + alias, err := h.db.CreateAliasInDomain(req.Source, req.Destination, domain.ID) if err != nil { Error(c, http.StatusInternalServerError, "failed to create alias") diff --git a/backend/internal/db/aliases.go b/backend/internal/db/aliases.go index e531103..4a2be2a 100644 --- a/backend/internal/db/aliases.go +++ b/backend/internal/db/aliases.go @@ -42,6 +42,14 @@ func (d *DB) GetAliasByID(id uint) (*Alias, error) { return &alias, nil } +func (d *DB) GetAliasBySource(source string) (*Alias, error) { + var alias Alias + if err := d.Where("source = ?", source).First(&alias).Error; err != nil { + return nil, err + } + return &alias, nil +} + func (d *DB) CreateAlias(source, destination string) (*Alias, error) { alias := Alias{ Source: source,