mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
* Add DenyLists parsing * Remove unneeded returned error * Plug global denylist into Command * app creation: apply denylist to backchannel logout URI * Inject denylist to backchannel logout worker * webhook config: validate against blocked URLs * Add notificationsWebhook denylist target * command: Add SMTP endpoint validation against blocklist * command: Add SMS endpoint validation against blocklist * Validate webhook endpoint against denylist on channel notification * Remove unused tests * handle deprecated denylists * remove unintended denylist entry in deprecated list * use single http client * fix tests * update comments * fixes * cleanup * address comments * fix merge --------- Co-authored-by: Livio Spring <[email protected]>
358 lines
10 KiB
Go
358 lines
10 KiB
Go
package handlers
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"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/channels/webhook"
|
|
"github.com/zitadel/zitadel/internal/notification/handlers/mock"
|
|
"github.com/zitadel/zitadel/internal/query"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
func TestNotificationQueries_GetActiveEmailConfig(t *testing.T) {
|
|
t.Parallel()
|
|
const instId = "instance-1"
|
|
const cryptAlg = "alg1"
|
|
const keyId = "key1"
|
|
const pwd = "password"
|
|
const clientSecret = "client-secret"
|
|
|
|
encryptedPwd := []byte("password-encrypted")
|
|
encryptedClientSecret := []byte("client-secret-encrypted")
|
|
|
|
var (
|
|
errDecryptionFailed = errors.New("decryption failed")
|
|
errDatabaseError = errors.New("database error")
|
|
)
|
|
|
|
tt := []struct {
|
|
name string
|
|
smtpConfig *query.SMTPConfig
|
|
smtpConfigErr error
|
|
setupCrypto func(m *crypto.MockEncryptionAlgorithm)
|
|
expected *email.Config
|
|
expectedErr error
|
|
}{
|
|
{
|
|
name: "smtp config with plain auth (user and password)",
|
|
smtpConfig: &query.SMTPConfig{
|
|
ID: "smtp1",
|
|
Description: "smtp config with plain auth (user and password)",
|
|
SMTPConfig: &query.SMTP{
|
|
TLS: true,
|
|
SenderAddress: "[email protected]",
|
|
SenderName: "sender",
|
|
ReplyToAddress: "[email protected]",
|
|
Host: "mail.com",
|
|
User: "mail-user",
|
|
PlainAuth: &query.PlainAuth{
|
|
Password: &crypto.CryptoValue{
|
|
CryptoType: 0,
|
|
Algorithm: cryptAlg,
|
|
KeyID: keyId,
|
|
Crypted: encryptedPwd,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
setupCrypto: func(m *crypto.MockEncryptionAlgorithm) {
|
|
m.EXPECT().Algorithm().Return(cryptAlg)
|
|
m.EXPECT().DecryptionKeyIDs().Return([]string{keyId})
|
|
m.EXPECT().DecryptString(gomock.Any(), gomock.Any()).Return(pwd, nil)
|
|
},
|
|
expected: &email.Config{
|
|
ProviderConfig: &email.Provider{
|
|
ID: "smtp1",
|
|
Description: "smtp config with plain auth (user and password)",
|
|
},
|
|
SMTPConfig: &smtp.Config{
|
|
SMTP: smtp.SMTP{
|
|
Host: "mail.com",
|
|
PlainAuth: &smtp.PlainAuthConfig{
|
|
User: "mail-user",
|
|
Password: pwd,
|
|
},
|
|
},
|
|
Tls: true,
|
|
From: "[email protected]",
|
|
FromName: "sender",
|
|
ReplyToAddress: "[email protected]",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "smtp config with user but no plain auth config (fallback to plain auth without password)",
|
|
smtpConfig: &query.SMTPConfig{
|
|
ID: "smtp2",
|
|
Description: "smtp config with user but no plain auth config (fallback to plain auth without 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 user but no plain auth config (fallback to plain auth without password)",
|
|
},
|
|
SMTPConfig: &smtp.Config{
|
|
SMTP: smtp.SMTP{
|
|
Host: "mail.com",
|
|
PlainAuth: &smtp.PlainAuthConfig{
|
|
User: "mail-user",
|
|
},
|
|
},
|
|
Tls: true,
|
|
From: "[email protected]",
|
|
FromName: "sender",
|
|
ReplyToAddress: "[email protected]",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "smtp config without authentication",
|
|
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]",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "smtp config with xoauth2 auth and client credentials",
|
|
smtpConfig: &query.SMTPConfig{
|
|
ID: "smtp4",
|
|
Description: "smtp config with xoauth2 auth and client credentials",
|
|
SMTPConfig: &query.SMTP{
|
|
SenderAddress: "[email protected]",
|
|
SenderName: "sender",
|
|
Host: "mail.com",
|
|
User: "mail-user",
|
|
XOAuth2Auth: &query.XOAuth2Auth{
|
|
TokenEndpoint: "https://token.example.com/token",
|
|
Scopes: []string{"https://mail.example.com/.default"},
|
|
ClientCredentials: &query.XOAuthClientCredentials{
|
|
ClientId: "client-id",
|
|
ClientSecret: &crypto.CryptoValue{
|
|
Algorithm: cryptAlg,
|
|
KeyID: keyId,
|
|
Crypted: encryptedClientSecret,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
setupCrypto: func(m *crypto.MockEncryptionAlgorithm) {
|
|
m.EXPECT().Algorithm().Return(cryptAlg)
|
|
m.EXPECT().DecryptionKeyIDs().Return([]string{keyId})
|
|
m.EXPECT().DecryptString(gomock.Any(), gomock.Any()).Return(clientSecret, nil)
|
|
},
|
|
expected: &email.Config{
|
|
ProviderConfig: &email.Provider{
|
|
ID: "smtp4",
|
|
Description: "smtp config with xoauth2 auth and client credentials",
|
|
},
|
|
SMTPConfig: &smtp.Config{
|
|
SMTP: smtp.SMTP{
|
|
Host: "mail.com",
|
|
XOAuth2Auth: &smtp.XOAuth2AuthConfig{
|
|
User: "mail-user",
|
|
TokenEndpoint: "https://token.example.com/token",
|
|
Scopes: []string{"https://mail.example.com/.default"},
|
|
ClientCredentialsAuth: &smtp.OAuth2ClientCredentials{
|
|
ClientId: "client-id",
|
|
ClientSecret: clientSecret,
|
|
},
|
|
},
|
|
},
|
|
From: "[email protected]",
|
|
FromName: "sender",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "smtp config with xoauth2 auth without client credentials",
|
|
smtpConfig: &query.SMTPConfig{
|
|
ID: "smtp5",
|
|
Description: "smtp config with xoauth2 auth without client credentials",
|
|
SMTPConfig: &query.SMTP{
|
|
SenderAddress: "[email protected]",
|
|
SenderName: "sender",
|
|
Host: "mail.com",
|
|
User: "mail-user",
|
|
XOAuth2Auth: &query.XOAuth2Auth{
|
|
TokenEndpoint: "https://token.example.com/token",
|
|
Scopes: []string{"scope1"},
|
|
},
|
|
},
|
|
},
|
|
expected: &email.Config{
|
|
ProviderConfig: &email.Provider{
|
|
ID: "smtp5",
|
|
Description: "smtp config with xoauth2 auth without client credentials",
|
|
},
|
|
SMTPConfig: &smtp.Config{
|
|
SMTP: smtp.SMTP{
|
|
Host: "mail.com",
|
|
XOAuth2Auth: &smtp.XOAuth2AuthConfig{
|
|
User: "mail-user",
|
|
TokenEndpoint: "https://token.example.com/token",
|
|
Scopes: []string{"scope1"},
|
|
},
|
|
},
|
|
From: "[email protected]",
|
|
FromName: "sender",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "plain auth password decryption error",
|
|
smtpConfig: &query.SMTPConfig{
|
|
ID: "smtp6",
|
|
Description: "plain auth password decryption error",
|
|
SMTPConfig: &query.SMTP{
|
|
Host: "mail.com",
|
|
User: "mail-user",
|
|
PlainAuth: &query.PlainAuth{
|
|
Password: &crypto.CryptoValue{
|
|
Algorithm: cryptAlg,
|
|
KeyID: keyId,
|
|
Crypted: encryptedPwd,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
setupCrypto: func(m *crypto.MockEncryptionAlgorithm) {
|
|
m.EXPECT().Algorithm().Return(cryptAlg)
|
|
m.EXPECT().DecryptionKeyIDs().Return([]string{keyId})
|
|
m.EXPECT().DecryptString(gomock.Any(), gomock.Any()).Return("", errDecryptionFailed)
|
|
},
|
|
expectedErr: errDecryptionFailed,
|
|
},
|
|
{
|
|
name: "xoauth2 client credentials decryption error",
|
|
smtpConfig: &query.SMTPConfig{
|
|
ID: "smtp7",
|
|
Description: "xoauth2 client credentials decryption error",
|
|
SMTPConfig: &query.SMTP{
|
|
Host: "mail.com",
|
|
User: "mail-user",
|
|
XOAuth2Auth: &query.XOAuth2Auth{
|
|
TokenEndpoint: "https://token.example.com/token",
|
|
ClientCredentials: &query.XOAuthClientCredentials{
|
|
ClientId: "client-id",
|
|
ClientSecret: &crypto.CryptoValue{
|
|
CryptoType: 0,
|
|
Algorithm: cryptAlg,
|
|
KeyID: keyId,
|
|
Crypted: encryptedClientSecret,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
setupCrypto: func(m *crypto.MockEncryptionAlgorithm) {
|
|
m.EXPECT().Algorithm().Return(cryptAlg)
|
|
m.EXPECT().DecryptionKeyIDs().Return([]string{keyId})
|
|
m.EXPECT().DecryptString(gomock.Any(), gomock.Any()).Return("", errDecryptionFailed)
|
|
},
|
|
expectedErr: errDecryptionFailed,
|
|
},
|
|
{
|
|
name: "http webhook config",
|
|
smtpConfig: &query.SMTPConfig{
|
|
ID: "http1",
|
|
Description: "http webhook config",
|
|
HTTPConfig: &query.HTTP{
|
|
Endpoint: "https://example.com/notify",
|
|
SigningKey: "my-signing-key",
|
|
},
|
|
},
|
|
expected: &email.Config{
|
|
ProviderConfig: &email.Provider{
|
|
ID: "http1",
|
|
Description: "http webhook config",
|
|
},
|
|
WebhookConfig: &webhook.Config{
|
|
CallURL: "https://example.com/notify",
|
|
Method: http.MethodPost,
|
|
SigningKey: "my-signing-key",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "neither smtp nor http config set",
|
|
smtpConfig: &query.SMTPConfig{
|
|
ID: "smtp8",
|
|
Description: "neither smtp nor http config set",
|
|
},
|
|
expectedErr: zerrors.ThrowNotFound(nil, "", ""),
|
|
},
|
|
{
|
|
name: "SMTPConfigActive query error",
|
|
smtpConfigErr: errDatabaseError,
|
|
expectedErr: errDatabaseError,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tt {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
ctrl := gomock.NewController(t)
|
|
ctx := authz.NewMockContext(instId, "org-1", "user-1")
|
|
|
|
cryptAlgMock := crypto.NewMockEncryptionAlgorithm(ctrl)
|
|
if tc.setupCrypto != nil {
|
|
tc.setupCrypto(cryptAlgMock)
|
|
}
|
|
|
|
queryMock := mock.NewMockQueries(ctrl)
|
|
queryMock.EXPECT().SMTPConfigActive(gomock.Any(), instId).Return(tc.smtpConfig, tc.smtpConfigErr)
|
|
|
|
notificationQueries := NewNotificationQueries(queryMock, &eventstore.Eventstore{}, "ext domain", uint16(1234), false, "filepath", nil, cryptAlgMock, nil, nil)
|
|
cfg, err := notificationQueries.GetActiveEmailConfig(ctx)
|
|
|
|
assert.ErrorIs(t, err, tc.expectedErr)
|
|
if tc.expectedErr != nil {
|
|
return
|
|
}
|
|
|
|
assert.EqualValues(t, tc.expected, cfg)
|
|
})
|
|
}
|
|
}
|