fix: smtp provider (#8610)

There were some problems after changing from SMTP providers to email
providers (https://github.com/zitadel/zitadel/pull/8545):
- panic (nil pointer) on SMTP provider retrieval for HTTP configs
- old SMTP configuration created before the multi SMTP configurations
(without id)
  - were not projected / listed
  - could not be always be activated
- Console treated HTTP configs as SMTP

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Stefan Benz 2024-09-13 15:22:25 +02:00 committed by GitHub
parent 3d87220180
commit 289378713e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 132 additions and 60 deletions

View File

@ -24,7 +24,7 @@
<th class="availability" mat-header-cell *matHeaderCellDef>
<span>{{ 'SMTP.LIST.ACTIVATED' | translate }}</span>
</th>
<td class="pointer availability" mat-cell *matCellDef="let config">
<td class="availability" [ngClass]="{ pointer: config.senderAddress }" mat-cell *matCellDef="let config">
<i
matTooltip="{{ 'SMTP.LIST.ACTIVATED' | translate }}"
*ngIf="isActive(config.state)"
@ -36,7 +36,7 @@
<ng-container matColumnDef="description">
<th mat-header-cell *matHeaderCellDef>{{ 'SETTING.SMTP.DESCRIPTION' | translate }}</th>
<td class="pointer" mat-cell *matCellDef="let config">
<td [ngClass]="{ pointer: config.senderAddress }" mat-cell *matCellDef="let config">
<span>{{ config?.description }}</span>
</td>
</ng-container>
@ -45,22 +45,22 @@
<th class="availability" mat-header-cell *matHeaderCellDef>
<span>TLS</span>
</th>
<td class="pointer availability" mat-cell *matCellDef="let config">
<td class="availability" [ngClass]="{ pointer: config.senderAddress }" mat-cell *matCellDef="let config">
<i *ngIf="config.tls" class="las la-lock"></i>
<i *ngIf="!config.tls" class="las la-unlock"></i>
<i *ngIf="config.senderAddress && !config.tls" class="las la-unlock"></i>
</td>
</ng-container>
<ng-container matColumnDef="host">
<th mat-header-cell *matHeaderCellDef>{{ 'SETTING.SMTP.HOSTANDPORT' | translate }}</th>
<td class="pointer" mat-cell *matCellDef="let config">
<td [ngClass]="{ pointer: config.senderAddress }" mat-cell *matCellDef="let config">
<span>{{ config?.host }}</span>
</td>
</ng-container>
<ng-container matColumnDef="senderAddress">
<th mat-header-cell *matHeaderCellDef>{{ 'SETTING.SMTP.SENDERADDRESS' | translate }}</th>
<td class="pointer" mat-cell *matCellDef="let config">
<td [ngClass]="{ pointer: config.senderAddress }" mat-cell *matCellDef="let config">
<span>{{ config?.senderAddress }}</span>
</td>
</ng-container>
@ -95,7 +95,7 @@
<button
actions
[disabled]="(['iam.write'] | hasRole | async) === false"
[disabled]="(['iam.write'] | hasRole | async) === false || !config?.senderAddress"
mat-icon-button
color="primary"
matTooltip="{{ 'SMTP.LIST.TEST' | translate }}"

View File

@ -205,6 +205,9 @@ export class SMTPTableComponent implements OnInit {
}
public navigateToProvider(row: SMTPConfig.AsObject) {
if (!row.senderAddress) {
return;
}
this.router.navigate(this.routerLinkForRow(row));
}
}

View File

@ -20,17 +20,20 @@ func listSMTPConfigsToModel(req *admin_pb.ListSMTPConfigsRequest) (*query.SMTPCo
}
func SMTPConfigToProviderPb(config *query.SMTPConfig) *settings_pb.SMTPConfig {
return &settings_pb.SMTPConfig{
Details: object.ToViewDetailsPb(config.Sequence, config.CreationDate, config.ChangeDate, config.ResourceOwner),
Id: config.ID,
Description: config.Description,
Tls: config.SMTPConfig.TLS,
Host: config.SMTPConfig.Host,
User: config.SMTPConfig.User,
State: SMTPConfigStateToPb(config.State),
SenderAddress: config.SMTPConfig.SenderAddress,
SenderName: config.SMTPConfig.SenderName,
ret := &settings_pb.SMTPConfig{
Details: object.ToViewDetailsPb(config.Sequence, config.CreationDate, config.ChangeDate, config.ResourceOwner),
Id: config.ID,
Description: config.Description,
State: SMTPConfigStateToPb(config.State),
}
if config.SMTPConfig != nil {
ret.Tls = config.SMTPConfig.TLS
ret.Host = config.SMTPConfig.Host
ret.User = config.SMTPConfig.User
ret.SenderAddress = config.SMTPConfig.SenderAddress
ret.SenderName = config.SMTPConfig.SenderName
}
return ret
}
func SMTPConfigsToPb(configs []*query.SMTPConfig) []*settings_pb.SMTPConfig {

View File

@ -104,6 +104,7 @@ func (wm *IAMSMTPConfigWriteModel) Reduce() error {
wm.reduceSMTPConfigRemovedEvent(e)
case *instance.SMTPConfigActivatedEvent:
if wm.ID != e.ID {
wm.State = domain.SMTPConfigStateInactive
continue
}
wm.State = domain.SMTPConfigStateActive

View File

@ -93,6 +93,7 @@ func (wm *IAMSMSConfigWriteModel) Reduce() error {
}
case *instance.SMSConfigTwilioActivatedEvent:
if wm.ID != e.ID {
wm.State = domain.SMSConfigStateInactive
continue
}
wm.State = domain.SMSConfigStateActive
@ -110,6 +111,7 @@ func (wm *IAMSMSConfigWriteModel) Reduce() error {
wm.State = domain.SMSConfigStateRemoved
case *instance.SMSConfigActivatedEvent:
if wm.ID != e.ID {
wm.State = domain.SMSConfigStateInactive
continue
}
wm.State = domain.SMSConfigStateActive

View File

@ -11,7 +11,7 @@ import (
)
const (
SMTPConfigProjectionTable = "projections.smtp_configs3"
SMTPConfigProjectionTable = "projections.smtp_configs4"
SMTPConfigTable = SMTPConfigProjectionTable + "_" + smtpConfigSMTPTableSuffix
SMTPConfigHTTPTable = SMTPConfigProjectionTable + "_" + smtpConfigHTTPTableSuffix
@ -174,7 +174,7 @@ func (p *smtpConfigProjection) reduceSMTPConfigAdded(event eventstore.Event) (*h
handler.AddCreateStatement(
[]handler.Column{
handler.NewCol(SMTPConfigSMTPColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCol(SMTPConfigSMTPColumnID, e.ID),
handler.NewCol(SMTPConfigSMTPColumnID, id),
handler.NewCol(SMTPConfigSMTPColumnTLS, e.TLS),
handler.NewCol(SMTPConfigSMTPColumnSenderAddress, e.SenderAddress),
handler.NewCol(SMTPConfigSMTPColumnSenderName, e.SenderName),

View File

@ -50,7 +50,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
@ -60,7 +60,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
},
},
{
expectedStmt: "UPDATE projections.smtp_configs3_smtp SET (tls, sender_address, sender_name, reply_to_address, host, username) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)",
expectedStmt: "UPDATE projections.smtp_configs4_smtp SET (tls, sender_address, sender_name, reply_to_address, host, username) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)",
expectedArgs: []interface{}{
true,
"sender",
@ -100,7 +100,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
@ -137,7 +137,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
@ -146,7 +146,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
},
},
{
expectedStmt: "UPDATE projections.smtp_configs3_smtp SET sender_address = $1 WHERE (id = $2) AND (instance_id = $3)",
expectedStmt: "UPDATE projections.smtp_configs4_smtp SET sender_address = $1 WHERE (id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
"sender",
"config-id",
@ -182,7 +182,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
@ -192,7 +192,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
},
},
{
expectedStmt: "UPDATE projections.smtp_configs3_http SET endpoint = $1 WHERE (id = $2) AND (instance_id = $3)",
expectedStmt: "UPDATE projections.smtp_configs4_http SET endpoint = $1 WHERE (id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
"endpoint",
"config-id",
@ -227,7 +227,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
@ -264,7 +264,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
@ -273,7 +273,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
},
},
{
expectedStmt: "UPDATE projections.smtp_configs3_http SET endpoint = $1 WHERE (id = $2) AND (instance_id = $3)",
expectedStmt: "UPDATE projections.smtp_configs4_http SET endpoint = $1 WHERE (id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
"endpoint",
"config-id",
@ -284,6 +284,69 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
},
},
},
{
name: "reduceSMTPConfigAdded (no id)",
args: args{
event: getEvent(
testEvent(
instance.SMTPConfigAddedEventType,
instance.AggregateType,
[]byte(`{
"instance_id": "instance-id",
"resource_owner": "ro-id",
"aggregate_id": "agg-id",
"tls": true,
"senderAddress": "sender",
"senderName": "name",
"replyToAddress": "reply-to",
"host": "host",
"user": "user",
"password": {
"cryptoType": 0,
"algorithm": "RSA-265",
"keyId": "key-id"
}
}`),
), eventstore.GenericEventMapper[instance.SMTPConfigAddedEvent]),
},
reduce: (&smtpConfigProjection{}).reduceSMTPConfigAdded,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.smtp_configs4 (creation_date, change_date, instance_id, resource_owner, aggregate_id, id, sequence, state, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedArgs: []interface{}{
anyArg{},
anyArg{},
"instance-id",
"ro-id",
"agg-id",
"ro-id",
uint64(15),
domain.SMTPConfigStateActive,
"generic",
},
},
{
expectedStmt: "INSERT INTO projections.smtp_configs4_smtp (instance_id, id, tls, sender_address, sender_name, reply_to_address, host, username, password) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedArgs: []interface{}{
"instance-id",
"ro-id",
true,
"sender",
"name",
"reply-to",
"host",
"user",
anyArg{},
},
},
},
},
},
},
{
name: "reduceSMTPConfigAdded",
args: args{
@ -318,7 +381,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.smtp_configs3 (creation_date, change_date, instance_id, resource_owner, aggregate_id, id, sequence, state, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedStmt: "INSERT INTO projections.smtp_configs4 (creation_date, change_date, instance_id, resource_owner, aggregate_id, id, sequence, state, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedArgs: []interface{}{
anyArg{},
anyArg{},
@ -332,7 +395,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
},
},
{
expectedStmt: "INSERT INTO projections.smtp_configs3_smtp (instance_id, id, tls, sender_address, sender_name, reply_to_address, host, username, password) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedStmt: "INSERT INTO projections.smtp_configs4_smtp (instance_id, id, tls, sender_address, sender_name, reply_to_address, host, username, password) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedArgs: []interface{}{
"instance-id",
"config-id",
@ -374,7 +437,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.smtp_configs3 (creation_date, change_date, instance_id, resource_owner, aggregate_id, id, sequence, state, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedStmt: "INSERT INTO projections.smtp_configs4 (creation_date, change_date, instance_id, resource_owner, aggregate_id, id, sequence, state, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedArgs: []interface{}{
anyArg{},
anyArg{},
@ -388,7 +451,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
},
},
{
expectedStmt: "INSERT INTO projections.smtp_configs3_http (instance_id, id, endpoint) VALUES ($1, $2, $3)",
expectedStmt: "INSERT INTO projections.smtp_configs4_http (instance_id, id, endpoint) VALUES ($1, $2, $3)",
expectedArgs: []interface{}{
"instance-id",
"config-id",
@ -420,7 +483,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (NOT (id = $4)) AND (state = $5) AND (instance_id = $6)",
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (NOT (id = $4)) AND (state = $5) AND (instance_id = $6)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
@ -431,7 +494,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
},
},
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
@ -465,7 +528,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
@ -505,7 +568,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3_smtp SET password = $1 WHERE (id = $2) AND (instance_id = $3)",
expectedStmt: "UPDATE projections.smtp_configs4_smtp SET password = $1 WHERE (id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
anyArg{},
"config-id",
@ -513,7 +576,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
},
},
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedStmt: "UPDATE projections.smtp_configs4 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
@ -546,7 +609,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.smtp_configs3 WHERE (id = $1) AND (instance_id = $2)",
expectedStmt: "DELETE FROM projections.smtp_configs4 WHERE (id = $1) AND (instance_id = $2)",
expectedArgs: []interface{}{
"config-id",
"instance-id",
@ -573,7 +636,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.smtp_configs3 WHERE (instance_id = $1)",
expectedStmt: "DELETE FROM projections.smtp_configs4 WHERE (instance_id = $1)",
expectedArgs: []interface{}{
"agg-id",
},

View File

@ -14,26 +14,26 @@ import (
)
var (
prepareSMTPConfigStmt = `SELECT projections.smtp_configs3.creation_date,` +
` projections.smtp_configs3.change_date,` +
` projections.smtp_configs3.resource_owner,` +
` projections.smtp_configs3.sequence,` +
` projections.smtp_configs3.id,` +
` projections.smtp_configs3.state,` +
` projections.smtp_configs3.description,` +
` projections.smtp_configs3_smtp.id,` +
` projections.smtp_configs3_smtp.tls,` +
` projections.smtp_configs3_smtp.sender_address,` +
` projections.smtp_configs3_smtp.sender_name,` +
` projections.smtp_configs3_smtp.reply_to_address,` +
` projections.smtp_configs3_smtp.host,` +
` projections.smtp_configs3_smtp.username,` +
` projections.smtp_configs3_smtp.password,` +
` projections.smtp_configs3_http.id,` +
` projections.smtp_configs3_http.endpoint` +
` FROM projections.smtp_configs3` +
` LEFT JOIN projections.smtp_configs3_smtp ON projections.smtp_configs3.id = projections.smtp_configs3_smtp.id AND projections.smtp_configs3.instance_id = projections.smtp_configs3_smtp.instance_id` +
` LEFT JOIN projections.smtp_configs3_http ON projections.smtp_configs3.id = projections.smtp_configs3_http.id AND projections.smtp_configs3.instance_id = projections.smtp_configs3_http.instance_id` +
prepareSMTPConfigStmt = `SELECT projections.smtp_configs4.creation_date,` +
` projections.smtp_configs4.change_date,` +
` projections.smtp_configs4.resource_owner,` +
` projections.smtp_configs4.sequence,` +
` projections.smtp_configs4.id,` +
` projections.smtp_configs4.state,` +
` projections.smtp_configs4.description,` +
` projections.smtp_configs4_smtp.id,` +
` projections.smtp_configs4_smtp.tls,` +
` projections.smtp_configs4_smtp.sender_address,` +
` projections.smtp_configs4_smtp.sender_name,` +
` projections.smtp_configs4_smtp.reply_to_address,` +
` projections.smtp_configs4_smtp.host,` +
` projections.smtp_configs4_smtp.username,` +
` projections.smtp_configs4_smtp.password,` +
` projections.smtp_configs4_http.id,` +
` projections.smtp_configs4_http.endpoint` +
` FROM projections.smtp_configs4` +
` LEFT JOIN projections.smtp_configs4_smtp ON projections.smtp_configs4.id = projections.smtp_configs4_smtp.id AND projections.smtp_configs4.instance_id = projections.smtp_configs4_smtp.instance_id` +
` LEFT JOIN projections.smtp_configs4_http ON projections.smtp_configs4.id = projections.smtp_configs4_http.id AND projections.smtp_configs4.instance_id = projections.smtp_configs4_http.instance_id` +
` AS OF SYSTEM TIME '-1 ms'`
prepareSMTPConfigCols = []string{
"creation_date",