diff --git a/docs/documentation/configuration/overview.md b/docs/documentation/configuration/overview.md index 0345338..44ce206 100644 --- a/docs/documentation/configuration/overview.md +++ b/docs/documentation/configuration/overview.md @@ -72,6 +72,7 @@ mail: auth_type: plain from: Wireguard Portal link_only: false + allow_peer_email: false auth: oidc: [] @@ -380,7 +381,9 @@ Controls how WireGuard Portal collects and reports usage statistics, including p ## Mail -Options for configuring email notifications or sending peer configurations via email. +Options for configuring email notifications or sending peer configurations via email. +By default, emails will only be sent to peers that have a valid user record linked. +To send emails to all peers that have a valid email-address as user-identifier, set `allow_peer_email` to `true`. ### `host` - **Default:** `127.0.0.1` @@ -418,6 +421,12 @@ Options for configuring email notifications or sending peer configurations via e - **Default:** `false` - **Description:** If `true`, emails only contain a link to WireGuard Portal, rather than attaching the full configuration. +### `allow_peer_email` +- **Default:** `false` + - **Description:** If `true`, and a peer has no valid user record linked, but the user-identifier of the peer is a valid email address, emails will be sent to that email address. + If false, and the peer has no valid user record linked, emails will not be sent. + If a peer has linked a valid user, the email address is always taken from the user record. + --- ## Auth diff --git a/internal/app/mail/manager.go b/internal/app/mail/manager.go index 7b9b3b9..21f50f3 100644 --- a/internal/app/mail/manager.go +++ b/internal/app/mail/manager.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "log/slog" + "net/mail" "github.com/h44z/wg-portal/internal/config" "github.com/h44z/wg-portal/internal/domain" @@ -101,29 +102,15 @@ func (m Manager) SendPeerEmail(ctx context.Context, linkOnly bool, style string, } if peer.UserIdentifier == "" { - slog.Debug("skipping peer email", - "peer", peerId, - "reason", "no user linked") - continue + return fmt.Errorf("peer %s has no user linked, no email is sent", peerId) } - user, err := m.users.GetUser(ctx, peer.UserIdentifier) - if err != nil { - slog.Debug("skipping peer email", - "peer", peerId, - "reason", "unable to fetch user", - "error", err) - continue + email, user := m.resolveEmail(ctx, peer) + if email == "" { + return fmt.Errorf("peer %s has no valid email address, no email is sent", peerId) } - if user.Email == "" { - slog.Debug("skipping peer email", - "peer", peerId, - "reason", "user has no mail address") - continue - } - - err = m.sendPeerEmail(ctx, linkOnly, style, user, peer) + err = m.sendPeerEmail(ctx, linkOnly, style, &user, peer) if err != nil { return fmt.Errorf("failed to send peer email for %s: %w", peerId, err) } @@ -194,3 +181,37 @@ func (m Manager) sendPeerEmail( return nil } + +func (m Manager) resolveEmail(ctx context.Context, peer *domain.Peer) (string, domain.User) { + user, err := m.users.GetUser(ctx, peer.UserIdentifier) + if err != nil { + if m.cfg.Mail.AllowPeerEmail { + _, err := mail.ParseAddress(string(peer.UserIdentifier)) // test if the user identifier is a valid email address + if err == nil { + slog.Debug("peer email: using user-identifier as email", + "peer", peer.Identifier, "email", peer.UserIdentifier) + return string(peer.UserIdentifier), domain.User{} + } else { + slog.Debug("peer email: skipping peer email", + "peer", peer.Identifier, + "reason", "peer has no user linked and user-identifier is not a valid email address") + return "", domain.User{} + } + } else { + slog.Debug("peer email: skipping peer email", + "peer", peer.Identifier, + "reason", "user has no user linked") + return "", domain.User{} + } + } + + if user.Email == "" { + slog.Debug("peer email: skipping peer email", + "peer", peer.Identifier, + "reason", "user has no mail address") + return "", domain.User{} + } + + slog.Debug("peer email: using user email", "peer", peer.Identifier, "email", user.Email) + return user.Email, *user +} diff --git a/internal/config/mail.go b/internal/config/mail.go index 9cc2b5b..56df2b0 100644 --- a/internal/config/mail.go +++ b/internal/config/mail.go @@ -41,4 +41,6 @@ type MailConfig struct { From string `yaml:"from"` // LinkOnly specifies whether emails should only contain a link to WireGuard Portal or attach the full configuration LinkOnly bool `yaml:"link_only"` + // AllowPeerEmail specifies whether emails should be sent to peers which have no valid user account linked, but an email address is set as "user". + AllowPeerEmail bool `yaml:"allow_peer_email"` }