Files
warmbly/internal/models/event_worker_validation.go
T

75 lines
3.1 KiB
Go

package models
import (
"strings"
"github.com/google/uuid"
)
type EventWorkerEmailValidation struct {
OrgID uuid.UUID `json:"org_id" avro:"org_id"`
ProcessID uuid.UUID `json:"process_id" avro:"process_id"`
Credentials *SmtpImap `json:"credentials" avro:"credentials"`
}
// Mail probe reasons: why one leg of a credential check did not pass. They
// travel from the worker to the backend inside EmailValidationVerdict and are
// what lets the connect form say what the server said instead of "invalid
// credentials" for everything from a typo to a firewall.
const (
// MailProbeAuthRefused: the server answered and refused the sign-in.
MailProbeAuthRefused = "auth_refused"
// MailProbeUnreachable: no connection at all (DNS, refused, filtered).
MailProbeUnreachable = "unreachable"
// MailProbeTLS: the handshake or certificate failed, or no STARTTLS.
MailProbeTLS = "tls"
// MailProbeTemporary: a 4xx-class refusal the server says to retry.
MailProbeTemporary = "temporary"
// MailProbeProtocol: the server spoke, but not something we can sign in to.
MailProbeProtocol = "protocol"
// MailProbeCleartext: the unencrypted mode is not allowed for this host.
MailProbeCleartext = "cleartext"
// MailProbeTimeout: the budget ran out before the server answered.
MailProbeTimeout = "timeout"
)
// EmailValidationLeg is one side (SMTP or IMAP) of a worker's verdict.
type EmailValidationLeg struct {
OK bool `json:"ok"`
Reason string `json:"reason,omitempty"`
// Detail is the server's own reply or the dial error, never anything of
// ours and never the password: it is shown to the person at the form.
Detail string `json:"detail,omitempty"`
// Port and Security are set when the worker reached the server on a port
// other than the one asked for: a 465 that never answered, taken on 587
// with STARTTLS. On a passing verdict the mailbox is stored with them.
Port int `json:"port,omitempty"`
Security string `json:"security,omitempty"`
}
// EmailValidationVerdict is the worker's answer to an EventWorkerEmailValidation,
// published as JSON on the process's Redis channel ahead of the legacy "1"/"0".
type EmailValidationVerdict struct {
OK bool `json:"ok"`
SMTP EmailValidationLeg `json:"smtp"`
IMAP EmailValidationLeg `json:"imap"`
// Error is set when the worker could not run the probes at all (it could
// not unseal the credentials, say), in closed words with the cause logged
// on the worker. The backend answers with a server error rather than
// letting an untested mailbox read as a mail server that never replied.
Error string `json:"error,omitempty"`
}
// GoogleMailHost reports whether host is one of Google's mail servers, where
// the password can only be an app password. Google prints those as four groups
// of four letters and accepts them without the spaces.
func GoogleMailHost(host string) bool {
h := strings.ToLower(strings.TrimSuffix(NormalizeMailHost(host), "."))
for _, d := range []string{"gmail.com", "googlemail.com", "google.com"} {
if h == d || strings.HasSuffix(h, "."+d) {
return true
}
}
return false
}