fix: allow for the smtp-config to have no password set. (#11193)

<!--
Please inform yourself about the contribution guidelines on submitting a
PR here:
https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr.
Take note of how PR/commit titles should be written and replace the
template texts in the sections below. Don't remove any of the sections.
It is important that the commit history clearly shows what is changed
and why.
Important: By submitting a contribution you agree to the terms from our
Licensing Policy as described here:
https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions.
-->

# Which Problems Are Solved

Adding a generic SMTP server using the gRPC endpoints resulted in an
error. This was because the password field was requried.


# How the Problems Are Solved

Removed the requirement for a password from the mail config.


# Additional Context

closes #11084
This commit is contained in:
Wim Van Laer
2026-01-13 07:36:33 +00:00
committed by GitHub
parent 26e7c8499d
commit c172adfc53
3 changed files with 162 additions and 8 deletions
@@ -14,8 +14,14 @@ type SMTP struct {
Password string
}
// HasAuth reports whether SMTP authentication should be attempted.
//
// It returns true when a User is configured, regardless of whether Password
// is set. This behavior is intentional to support SMTP servers that allow
// authentication without a password. For servers that require a password,
// this means authentication may still be attempted with an empty password.
func (smtp *SMTP) HasAuth() bool {
return smtp.User != "" && smtp.Password != ""
return smtp.User != ""
}
type ConfigHTTP struct {
@@ -12,7 +12,7 @@ import (
"github.com/zitadel/zitadel/internal/zerrors"
)
// GetSMTPConfig reads the iam SMTP provider config
// GetActiveEmailConfig reads the iam SMTP provider config
func (n *NotificationQueries) GetActiveEmailConfig(ctx context.Context) (*email.Config, error) {
config, err := n.SMTPConfigActive(ctx, authz.GetInstance(ctx).InstanceID())
if err != nil {
@@ -23,13 +23,14 @@ func (n *NotificationQueries) GetActiveEmailConfig(ctx context.Context) (*email.
Description: config.Description,
}
if config.SMTPConfig != nil {
if config.SMTPConfig.Password == nil {
return nil, zerrors.ThrowNotFound(err, "QUERY-Wrs3gw", "Errors.SMTPConfig.NotFound")
}
password, err := crypto.DecryptString(config.SMTPConfig.Password, n.SMTPPasswordCrypto)
if err != nil {
return nil, err
var password string
if config.SMTPConfig.Password != nil {
password, err = crypto.DecryptString(config.SMTPConfig.Password, n.SMTPPasswordCrypto)
if err != nil {
return nil, err
}
}
return &email.Config{
ProviderConfig: provider,
SMTPConfig: &smtp.Config{
@@ -0,0 +1,147 @@
package handlers
import (
"testing"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/notification/channels/email"
"github.com/zitadel/zitadel/internal/notification/channels/smtp"
"github.com/zitadel/zitadel/internal/notification/handlers/mock"
"github.com/zitadel/zitadel/internal/query"
)
func TestNotificationQueries_GetActiveEmailConfig(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
const instId = "instance-1"
const cryptAlg = "alg1"
const keyId = "key1"
const pwd = "password"
encryptedPwd := []byte("password-encrypted")
cryptAlgMock := crypto.NewMockEncryptionAlgorithm(ctrl)
cryptAlgMock.EXPECT().DecryptString(gomock.Any(), gomock.Any()).Return(pwd, nil)
cryptAlgMock.EXPECT().Algorithm().Return(cryptAlg)
cryptAlgMock.EXPECT().DecryptionKeyIDs().Return([]string{keyId})
tt := []struct {
smtpConfig *query.SMTPConfig
expected *email.Config
}{
{
smtpConfig: &query.SMTPConfig{
ID: "smtp1",
Description: "smtp config with authentication (user and password)",
SMTPConfig: &query.SMTP{
TLS: true,
SenderAddress: "[email protected]",
SenderName: "sender",
ReplyToAddress: "[email protected]",
Host: "mail.com",
User: "mail-user",
Password: &crypto.CryptoValue{
CryptoType: 0,
Algorithm: cryptAlg,
KeyID: keyId,
Crypted: encryptedPwd,
},
},
},
expected: &email.Config{
ProviderConfig: &email.Provider{
ID: "smtp1",
Description: "smtp config with authentication (user and password)",
},
SMTPConfig: &smtp.Config{
SMTP: smtp.SMTP{
Host: "mail.com",
User: "mail-user",
Password: pwd,
},
Tls: true,
From: "[email protected]",
FromName: "sender",
ReplyToAddress: "[email protected]",
},
},
},
{
smtpConfig: &query.SMTPConfig{
ID: "smtp2",
Description: "smtp config with authentication (user and no password)",
SMTPConfig: &query.SMTP{
TLS: true,
SenderAddress: "[email protected]",
SenderName: "sender",
ReplyToAddress: "[email protected]",
Host: "mail.com",
User: "mail-user",
},
},
expected: &email.Config{
ProviderConfig: &email.Provider{
ID: "smtp2",
Description: "smtp config with authentication (user and no password)",
},
SMTPConfig: &smtp.Config{
SMTP: smtp.SMTP{
Host: "mail.com",
User: "mail-user",
},
Tls: true,
From: "[email protected]",
FromName: "sender",
ReplyToAddress: "[email protected]",
},
},
},
{
smtpConfig: &query.SMTPConfig{
ID: "smtp3",
Description: "smtp config without authentication",
SMTPConfig: &query.SMTP{
TLS: true,
SenderAddress: "[email protected]",
SenderName: "sender",
ReplyToAddress: "[email protected]",
Host: "mail.com",
},
},
expected: &email.Config{
ProviderConfig: &email.Provider{
ID: "smtp3",
Description: "smtp config without authentication",
},
SMTPConfig: &smtp.Config{
SMTP: smtp.SMTP{
Host: "mail.com",
},
Tls: true,
From: "[email protected]",
FromName: "sender",
ReplyToAddress: "[email protected]",
},
},
},
}
for _, tc := range tt {
t.Run(tc.smtpConfig.Description, func(t *testing.T) {
ctx := authz.NewMockContext(instId, "org-1", "user-1")
queryMock := mock.NewMockQueries(ctrl)
queryMock.EXPECT().SMTPConfigActive(gomock.Any(), instId).Return(tc.smtpConfig, nil)
notificationQueries := NewNotificationQueries(queryMock, &eventstore.Eventstore{}, "ext domain", uint16(1234), false, "filepath", nil, cryptAlgMock, nil)
cfg, err := notificationQueries.GetActiveEmailConfig(ctx)
assert.NoError(t, err)
assert.EqualValues(t, tc.expected, cfg)
})
}
}