feat: send test mail (#7968)

# Which Problems Are Solved

- Zitadel doesn't have a way to test SMTP settings either before
creating a new provider or once the SMTP provider has been created.
- Zitadel SMTP messages can be more informative for usual errors

# How the Problems Are Solved

- A new step is added to the new/update SMTP provider wizard that allows
us to test a configuration. The result is shown in a text area.
- From the table of SMTP providers you can test your settings too.
- The email address to send the email is by default the email address
for the logged in user as suggested.
- Some of the SMTP error messages have been changed to give more
information about the possible situation. For example: could not contact
with the SMTP server, check the port, firewall issues... instead of
could not dial

Here's a video showing this new option in action:


https://github.com/zitadel/zitadel/assets/30386061/50128ba1-c9fa-4481-8eec-e79a3ca69bda

# Additional Changes

Replace this example text with a concise list of additional changes that
this PR introduces, that are not directly solving the initial problem
but are related.
For example:
- The docs explicitly describe that the property XY is mandatory
- Adds missing translations for validations.

# Additional Context

- Closes #4504
This commit is contained in:
Miguel Cabrerizo
2024-06-20 21:51:42 +02:00
committed by GitHub
parent 00b5e55565
commit 3635320ce8
51 changed files with 1362 additions and 46 deletions

View File

@@ -285,6 +285,89 @@ func (c *Commands) RemoveSMTPConfig(ctx context.Context, instanceID, id string)
return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
}
func (c *Commands) TestSMTPConfig(ctx context.Context, instanceID, id, email string, config *smtp.Config) error {
password := config.SMTP.Password
if email == "" {
return zerrors.ThrowInvalidArgument(nil, "SMTP-p9uy", "Errors.SMTPConfig.TestEmailNotFound")
}
if id == "" && password == "" {
return zerrors.ThrowInvalidArgument(nil, "SMTP-p9kj", "Errors.SMTPConfig.TestPassword")
}
// If the password is not sent it'd mean that the password hasn't been changed for
// the stored configuration identified by its id so we can try to retrieve it
if id != "" && password == "" {
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, "")
if err != nil {
return err
}
if !smtpConfigWriteModel.State.Exists() {
return zerrors.ThrowNotFound(nil, "SMTP-p9cc", "Errors.SMTPConfig.NotFound")
}
password, err = crypto.DecryptString(smtpConfigWriteModel.Password, c.smtpEncryption)
if err != nil {
return err
}
}
config.SMTP.Password = password
// Try to send an email
err := smtp.TestConfiguration(config, email)
if err != nil {
return err
}
return nil
}
func (c *Commands) TestSMTPConfigById(ctx context.Context, instanceID, id, email string) error {
if id == "" {
return zerrors.ThrowInvalidArgument(nil, "SMTP-99oki", "Errors.IDMissing")
}
if email == "" {
return zerrors.ThrowInvalidArgument(nil, "SMTP-99yth", "Errors.SMTPConfig.TestEmailNotFound")
}
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, "")
if err != nil {
return err
}
if !smtpConfigWriteModel.State.Exists() {
return zerrors.ThrowNotFound(nil, "SMTP-99klw", "Errors.SMTPConfig.NotFound")
}
password, err := crypto.DecryptString(smtpConfigWriteModel.Password, c.smtpEncryption)
if err != nil {
return err
}
smtpConfig := &smtp.Config{
Description: smtpConfigWriteModel.Description,
Tls: smtpConfigWriteModel.TLS,
From: smtpConfigWriteModel.SenderAddress,
FromName: smtpConfigWriteModel.SenderName,
SMTP: smtp.SMTP{
Host: smtpConfigWriteModel.Host,
User: smtpConfigWriteModel.User,
Password: password,
},
}
// Try to send an email
err = smtp.TestConfiguration(smtpConfig, email)
if err != nil {
return err
}
return nil
}
func checkSenderAddress(writeModel *IAMSMTPConfigWriteModel) error {
if !writeModel.smtpSenderAddressMatchesInstanceDomain {
return nil