feat: add http as smtp provider (#8545)

# Which Problems Are Solved

Send Email messages as a HTTP call to a relay, for own logic on handling
different Email providers

# How the Problems Are Solved

Create endpoints under Email provider to manage SMTP and HTTP in the
notification handlers.

# Additional Changes

Clean up old logic in command and query side to handle the general Email
providers with deactivate, activate and remove.

# Additional Context

Partially closes #8270

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Stefan Benz 2024-09-12 06:27:29 +02:00 committed by GitHub
parent d8a71d217c
commit 21c38b061d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 3575 additions and 1152 deletions

View File

@ -0,0 +1,140 @@
package admin
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/grpc/object"
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
)
func (s *Server) GetEmailProvider(ctx context.Context, req *admin_pb.GetEmailProviderRequest) (*admin_pb.GetEmailProviderResponse, error) {
smtp, err := s.query.SMTPConfigActive(ctx, authz.GetInstance(ctx).InstanceID())
if err != nil {
return nil, err
}
return &admin_pb.GetEmailProviderResponse{
Config: emailProviderToProviderPb(smtp),
}, nil
}
func (s *Server) GetEmailProviderById(ctx context.Context, req *admin_pb.GetEmailProviderByIdRequest) (*admin_pb.GetEmailProviderByIdResponse, error) {
smtp, err := s.query.SMTPConfigByID(ctx, authz.GetInstance(ctx).InstanceID(), req.Id)
if err != nil {
return nil, err
}
return &admin_pb.GetEmailProviderByIdResponse{
Config: emailProviderToProviderPb(smtp),
}, nil
}
func (s *Server) AddEmailProviderSMTP(ctx context.Context, req *admin_pb.AddEmailProviderSMTPRequest) (*admin_pb.AddEmailProviderSMTPResponse, error) {
config := addEmailProviderSMTPToConfig(ctx, req)
if err := s.command.AddSMTPConfig(ctx, config); err != nil {
return nil, err
}
return &admin_pb.AddEmailProviderSMTPResponse{
Details: object.DomainToChangeDetailsPb(config.Details),
Id: config.ID,
}, nil
}
func (s *Server) UpdateEmailProviderSMTP(ctx context.Context, req *admin_pb.UpdateEmailProviderSMTPRequest) (*admin_pb.UpdateEmailProviderSMTPResponse, error) {
config := updateEmailProviderSMTPToConfig(ctx, req)
if err := s.command.ChangeSMTPConfig(ctx, config); err != nil {
return nil, err
}
return &admin_pb.UpdateEmailProviderSMTPResponse{
Details: object.DomainToChangeDetailsPb(config.Details),
}, nil
}
func (s *Server) AddEmailProviderHTTP(ctx context.Context, req *admin_pb.AddEmailProviderHTTPRequest) (*admin_pb.AddEmailProviderHTTPResponse, error) {
config := addEmailProviderHTTPToConfig(ctx, req)
if err := s.command.AddSMTPConfigHTTP(ctx, config); err != nil {
return nil, err
}
return &admin_pb.AddEmailProviderHTTPResponse{
Details: object.DomainToChangeDetailsPb(config.Details),
Id: config.ID,
}, nil
}
func (s *Server) UpdateEmailProviderHTTP(ctx context.Context, req *admin_pb.UpdateEmailProviderHTTPRequest) (*admin_pb.UpdateEmailProviderHTTPResponse, error) {
config := updateEmailProviderHTTPToConfig(ctx, req)
if err := s.command.ChangeSMTPConfigHTTP(ctx, config); err != nil {
return nil, err
}
return &admin_pb.UpdateEmailProviderHTTPResponse{
Details: object.DomainToChangeDetailsPb(config.Details),
}, nil
}
func (s *Server) RemoveEmailProvider(ctx context.Context, req *admin_pb.RemoveEmailProviderRequest) (*admin_pb.RemoveEmailProviderResponse, error) {
details, err := s.command.RemoveSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id)
if err != nil {
return nil, err
}
return &admin_pb.RemoveEmailProviderResponse{
Details: object.DomainToChangeDetailsPb(details),
}, nil
}
func (s *Server) UpdateEmailProviderSMTPPassword(ctx context.Context, req *admin_pb.UpdateEmailProviderSMTPPasswordRequest) (*admin_pb.UpdateEmailProviderSMTPPasswordResponse, error) {
details, err := s.command.ChangeSMTPConfigPassword(ctx, authz.GetInstance(ctx).InstanceID(), req.Id, req.Password)
if err != nil {
return nil, err
}
return &admin_pb.UpdateEmailProviderSMTPPasswordResponse{
Details: object.DomainToChangeDetailsPb(details),
}, nil
}
func (s *Server) ListEmailProviders(ctx context.Context, req *admin_pb.ListEmailProvidersRequest) (*admin_pb.ListEmailProvidersResponse, error) {
queries, err := listEmailProvidersToModel(req)
if err != nil {
return nil, err
}
result, err := s.query.SearchSMTPConfigs(ctx, queries)
if err != nil {
return nil, err
}
return &admin_pb.ListEmailProvidersResponse{
Details: object.ToListDetails(result.Count, result.Sequence, result.LastRun),
Result: emailProvidersToPb(result.Configs),
}, nil
}
func (s *Server) ActivateEmailProvider(ctx context.Context, req *admin_pb.ActivateEmailProviderRequest) (*admin_pb.ActivateEmailProviderResponse, error) {
result, err := s.command.ActivateSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id)
if err != nil {
return nil, err
}
return &admin_pb.ActivateEmailProviderResponse{
Details: object.DomainToAddDetailsPb(result),
}, nil
}
func (s *Server) DeactivateEmailProvider(ctx context.Context, req *admin_pb.DeactivateEmailProviderRequest) (*admin_pb.DeactivateEmailProviderResponse, error) {
result, err := s.command.DeactivateSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id)
if err != nil {
return nil, err
}
return &admin_pb.DeactivateEmailProviderResponse{
Details: object.DomainToAddDetailsPb(result),
}, nil
}
func (s *Server) TestEmailProviderById(ctx context.Context, req *admin_pb.TestEmailProviderSMTPByIdRequest) (*admin_pb.TestEmailProviderSMTPByIdResponse, error) {
if err := s.command.TestSMTPConfigById(ctx, authz.GetInstance(ctx).InstanceID(), req.Id, req.ReceiverAddress); err != nil {
return nil, err
}
return &admin_pb.TestEmailProviderSMTPByIdResponse{}, nil
}
func (s *Server) TestEmailProviderSMTP(ctx context.Context, req *admin_pb.TestEmailProviderSMTPRequest) (*admin_pb.TestEmailProviderSMTPResponse, error) {
if err := s.command.TestSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id, req.ReceiverAddress, testEmailProviderSMTPToConfig(req)); err != nil {
return nil, err
}
return &admin_pb.TestEmailProviderSMTPResponse{}, nil
}

View File

@ -0,0 +1,145 @@
package admin
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/grpc/object"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/notification/channels/smtp"
"github.com/zitadel/zitadel/internal/query"
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
settings_pb "github.com/zitadel/zitadel/pkg/grpc/settings"
)
func listEmailProvidersToModel(req *admin_pb.ListEmailProvidersRequest) (*query.SMTPConfigsSearchQueries, error) {
offset, limit, asc := object.ListQueryToModel(req.Query)
return &query.SMTPConfigsSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
},
}, nil
}
func emailProvidersToPb(configs []*query.SMTPConfig) []*settings_pb.EmailProvider {
c := make([]*settings_pb.EmailProvider, len(configs))
for i, config := range configs {
c[i] = emailProviderToProviderPb(config)
}
return c
}
func emailProviderToProviderPb(config *query.SMTPConfig) *settings_pb.EmailProvider {
return &settings_pb.EmailProvider{
Details: object.ToViewDetailsPb(config.Sequence, config.CreationDate, config.ChangeDate, config.ResourceOwner),
Id: config.ID,
Description: config.Description,
State: emailProviderStateToPb(config.State),
Config: emailProviderToPb(config),
}
}
func emailProviderStateToPb(state domain.SMTPConfigState) settings_pb.EmailProviderState {
switch state {
case domain.SMTPConfigStateUnspecified, domain.SMTPConfigStateRemoved:
return settings_pb.EmailProviderState_EMAIL_PROVIDER_STATE_UNSPECIFIED
case domain.SMTPConfigStateActive:
return settings_pb.EmailProviderState_EMAIL_PROVIDER_ACTIVE
case domain.SMTPConfigStateInactive:
return settings_pb.EmailProviderState_EMAIL_PROVIDER_INACTIVE
default:
return settings_pb.EmailProviderState_EMAIL_PROVIDER_STATE_UNSPECIFIED
}
}
func emailProviderToPb(config *query.SMTPConfig) settings_pb.EmailConfig {
if config.SMTPConfig != nil {
return smtpToPb(config.SMTPConfig)
}
if config.HTTPConfig != nil {
return httpToPb(config.HTTPConfig)
}
return nil
}
func httpToPb(http *query.HTTP) *settings_pb.EmailProvider_Http {
return &settings_pb.EmailProvider_Http{
Http: &settings_pb.EmailProviderHTTP{
Endpoint: http.Endpoint,
},
}
}
func smtpToPb(config *query.SMTP) *settings_pb.EmailProvider_Smtp {
return &settings_pb.EmailProvider_Smtp{
Smtp: &settings_pb.EmailProviderSMTP{
Tls: config.TLS,
Host: config.Host,
User: config.User,
SenderAddress: config.SenderAddress,
SenderName: config.SenderName,
},
}
}
func addEmailProviderSMTPToConfig(ctx context.Context, req *admin_pb.AddEmailProviderSMTPRequest) *command.AddSMTPConfig {
return &command.AddSMTPConfig{
ResourceOwner: authz.GetInstance(ctx).InstanceID(),
Description: req.Description,
Tls: req.Tls,
From: req.SenderAddress,
FromName: req.SenderName,
ReplyToAddress: req.ReplyToAddress,
Host: req.Host,
User: req.User,
Password: req.Password,
}
}
func updateEmailProviderSMTPToConfig(ctx context.Context, req *admin_pb.UpdateEmailProviderSMTPRequest) *command.ChangeSMTPConfig {
return &command.ChangeSMTPConfig{
ResourceOwner: authz.GetInstance(ctx).InstanceID(),
ID: req.Id,
Description: req.Description,
Tls: req.Tls,
From: req.SenderAddress,
FromName: req.SenderName,
ReplyToAddress: req.ReplyToAddress,
Host: req.Host,
User: req.User,
Password: req.Password,
}
}
func addEmailProviderHTTPToConfig(ctx context.Context, req *admin_pb.AddEmailProviderHTTPRequest) *command.AddSMTPConfigHTTP {
return &command.AddSMTPConfigHTTP{
ResourceOwner: authz.GetInstance(ctx).InstanceID(),
Description: req.Description,
Endpoint: req.Endpoint,
}
}
func updateEmailProviderHTTPToConfig(ctx context.Context, req *admin_pb.UpdateEmailProviderHTTPRequest) *command.ChangeSMTPConfigHTTP {
return &command.ChangeSMTPConfigHTTP{
ResourceOwner: authz.GetInstance(ctx).InstanceID(),
ID: req.Id,
Description: req.Description,
Endpoint: req.Endpoint,
}
}
func testEmailProviderSMTPToConfig(req *admin_pb.TestEmailProviderSMTPRequest) *smtp.Config {
return &smtp.Config{
Tls: req.Tls,
From: req.SenderAddress,
FromName: req.SenderName,
SMTP: smtp.SMTP{
Host: req.Host,
User: req.User,
Password: req.Password,
},
}
}

View File

@ -1,14 +1,16 @@
package admin package admin
import ( import (
"context"
"google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/durationpb"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/grpc/object" "github.com/zitadel/zitadel/internal/api/grpc/object"
obj_grpc "github.com/zitadel/zitadel/internal/api/grpc/object" obj_grpc "github.com/zitadel/zitadel/internal/api/grpc/object"
"github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/notification/channels/smtp"
"github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/zerrors" "github.com/zitadel/zitadel/internal/zerrors"
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin" admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
@ -131,50 +133,51 @@ func SecretGeneratorTypeToDomain(generatorType settings_pb.SecretGeneratorType)
} }
} }
func AddSMTPToConfig(req *admin_pb.AddSMTPConfigRequest) *smtp.Config { func addSMTPToConfig(ctx context.Context, req *admin_pb.AddSMTPConfigRequest) *command.AddSMTPConfig {
return &smtp.Config{ return &command.AddSMTPConfig{
ResourceOwner: authz.GetInstance(ctx).InstanceID(),
Description: req.Description, Description: req.Description,
Tls: req.Tls, Tls: req.Tls,
From: req.SenderAddress, From: req.SenderAddress,
FromName: req.SenderName, FromName: req.SenderName,
ReplyToAddress: req.ReplyToAddress, ReplyToAddress: req.ReplyToAddress,
SMTP: smtp.SMTP{ Host: req.Host,
Host: req.Host, User: req.User,
User: req.User, Password: req.Password,
Password: req.Password,
},
} }
} }
func UpdateSMTPToConfig(req *admin_pb.UpdateSMTPConfigRequest) *smtp.Config { func updateSMTPToConfig(ctx context.Context, req *admin_pb.UpdateSMTPConfigRequest) *command.ChangeSMTPConfig {
return &smtp.Config{ return &command.ChangeSMTPConfig{
ResourceOwner: authz.GetInstance(ctx).InstanceID(),
ID: req.Id,
Description: req.Description, Description: req.Description,
Tls: req.Tls, Tls: req.Tls,
From: req.SenderAddress, From: req.SenderAddress,
FromName: req.SenderName, FromName: req.SenderName,
ReplyToAddress: req.ReplyToAddress, ReplyToAddress: req.ReplyToAddress,
SMTP: smtp.SMTP{ Host: req.Host,
Host: req.Host, User: req.User,
User: req.User, Password: req.Password,
Password: req.Password,
},
} }
} }
func SMTPConfigToPb(smtp *query.SMTPConfig) *settings_pb.SMTPConfig { func SMTPConfigToPb(smtp *query.SMTPConfig) *settings_pb.SMTPConfig {
mapped := &settings_pb.SMTPConfig{ if smtp.SMTPConfig != nil {
Description: smtp.Description, return &settings_pb.SMTPConfig{
Tls: smtp.TLS, Description: smtp.Description,
SenderAddress: smtp.SenderAddress, Tls: smtp.SMTPConfig.TLS,
SenderName: smtp.SenderName, SenderAddress: smtp.SMTPConfig.SenderAddress,
ReplyToAddress: smtp.ReplyToAddress, SenderName: smtp.SMTPConfig.SenderName,
Host: smtp.Host, ReplyToAddress: smtp.SMTPConfig.ReplyToAddress,
User: smtp.User, Host: smtp.SMTPConfig.Host,
Details: obj_grpc.ToViewDetailsPb(smtp.Sequence, smtp.CreationDate, smtp.ChangeDate, smtp.ResourceOwner), User: smtp.SMTPConfig.User,
Id: smtp.ID, Details: obj_grpc.ToViewDetailsPb(smtp.Sequence, smtp.CreationDate, smtp.ChangeDate, smtp.ResourceOwner),
State: settings_pb.SMTPConfigState(smtp.State), Id: smtp.ID,
State: settings_pb.SMTPConfigState(smtp.State),
}
} }
return mapped return nil
} }
func SecurityPolicyToPb(policy *query.SecurityPolicy) *settings_pb.SecurityPolicy { func SecurityPolicyToPb(policy *query.SecurityPolicy) *settings_pb.SecurityPolicy {

View File

@ -20,10 +20,7 @@ func (s *Server) GetSMTPConfig(ctx context.Context, req *admin_pb.GetSMTPConfigR
} }
func (s *Server) GetSMTPConfigById(ctx context.Context, req *admin_pb.GetSMTPConfigByIdRequest) (*admin_pb.GetSMTPConfigByIdResponse, error) { func (s *Server) GetSMTPConfigById(ctx context.Context, req *admin_pb.GetSMTPConfigByIdRequest) (*admin_pb.GetSMTPConfigByIdResponse, error) {
instanceID := authz.GetInstance(ctx).InstanceID() smtp, err := s.query.SMTPConfigByID(ctx, authz.GetInstance(ctx).InstanceID(), req.Id)
resourceOwner := instanceID // Will be replaced when orgs have smtp configs
smtp, err := s.query.SMTPConfigByID(ctx, instanceID, resourceOwner, req.Id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -33,29 +30,23 @@ func (s *Server) GetSMTPConfigById(ctx context.Context, req *admin_pb.GetSMTPCon
} }
func (s *Server) AddSMTPConfig(ctx context.Context, req *admin_pb.AddSMTPConfigRequest) (*admin_pb.AddSMTPConfigResponse, error) { func (s *Server) AddSMTPConfig(ctx context.Context, req *admin_pb.AddSMTPConfigRequest) (*admin_pb.AddSMTPConfigResponse, error) {
id, details, err := s.command.AddSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), AddSMTPToConfig(req)) config := addSMTPToConfig(ctx, req)
if err != nil { if err := s.command.AddSMTPConfig(ctx, config); err != nil {
return nil, err return nil, err
} }
return &admin_pb.AddSMTPConfigResponse{ return &admin_pb.AddSMTPConfigResponse{
Details: object.ChangeToDetailsPb( Details: object.DomainToChangeDetailsPb(config.Details),
details.Sequence, Id: config.ID,
details.EventDate,
details.ResourceOwner),
Id: id,
}, nil }, nil
} }
func (s *Server) UpdateSMTPConfig(ctx context.Context, req *admin_pb.UpdateSMTPConfigRequest) (*admin_pb.UpdateSMTPConfigResponse, error) { func (s *Server) UpdateSMTPConfig(ctx context.Context, req *admin_pb.UpdateSMTPConfigRequest) (*admin_pb.UpdateSMTPConfigResponse, error) {
details, err := s.command.ChangeSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id, UpdateSMTPToConfig(req)) config := updateSMTPToConfig(ctx, req)
if err != nil { if err := s.command.ChangeSMTPConfig(ctx, config); err != nil {
return nil, err return nil, err
} }
return &admin_pb.UpdateSMTPConfigResponse{ return &admin_pb.UpdateSMTPConfigResponse{
Details: object.ChangeToDetailsPb( Details: object.DomainToChangeDetailsPb(config.Details),
details.Sequence,
details.EventDate,
details.ResourceOwner),
}, nil }, nil
} }
@ -65,10 +56,7 @@ func (s *Server) RemoveSMTPConfig(ctx context.Context, req *admin_pb.RemoveSMTPC
return nil, err return nil, err
} }
return &admin_pb.RemoveSMTPConfigResponse{ return &admin_pb.RemoveSMTPConfigResponse{
Details: object.ChangeToDetailsPb( Details: object.DomainToChangeDetailsPb(details),
details.Sequence,
details.EventDate,
details.ResourceOwner),
}, nil }, nil
} }
@ -78,10 +66,7 @@ func (s *Server) UpdateSMTPConfigPassword(ctx context.Context, req *admin_pb.Upd
return nil, err return nil, err
} }
return &admin_pb.UpdateSMTPConfigPasswordResponse{ return &admin_pb.UpdateSMTPConfigPasswordResponse{
Details: object.ChangeToDetailsPb( Details: object.DomainToChangeDetailsPb(details),
details.Sequence,
details.EventDate,
details.ResourceOwner),
}, nil }, nil
} }
@ -101,19 +86,11 @@ func (s *Server) ListSMTPConfigs(ctx context.Context, req *admin_pb.ListSMTPConf
} }
func (s *Server) ActivateSMTPConfig(ctx context.Context, req *admin_pb.ActivateSMTPConfigRequest) (*admin_pb.ActivateSMTPConfigResponse, error) { func (s *Server) ActivateSMTPConfig(ctx context.Context, req *admin_pb.ActivateSMTPConfigRequest) (*admin_pb.ActivateSMTPConfigResponse, error) {
// Get the ID of current SMTP active provider if any result, err := s.command.ActivateSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id)
currentActiveProviderID := ""
smtp, err := s.query.SMTPConfigActive(ctx, authz.GetInstance(ctx).InstanceID())
if err == nil {
currentActiveProviderID = smtp.ID
}
result, err := s.command.ActivateSMTPConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id, currentActiveProviderID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &admin_pb.ActivateSMTPConfigResponse{ return &admin_pb.ActivateSMTPConfigResponse{
Details: object.DomainToAddDetailsPb(result), Details: object.DomainToAddDetailsPb(result),
}, nil }, nil

View File

@ -2,6 +2,7 @@ package admin
import ( import (
"github.com/zitadel/zitadel/internal/api/grpc/object" "github.com/zitadel/zitadel/internal/api/grpc/object"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/query"
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin" admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
settings_pb "github.com/zitadel/zitadel/pkg/grpc/settings" settings_pb "github.com/zitadel/zitadel/pkg/grpc/settings"
@ -23,12 +24,12 @@ func SMTPConfigToProviderPb(config *query.SMTPConfig) *settings_pb.SMTPConfig {
Details: object.ToViewDetailsPb(config.Sequence, config.CreationDate, config.ChangeDate, config.ResourceOwner), Details: object.ToViewDetailsPb(config.Sequence, config.CreationDate, config.ChangeDate, config.ResourceOwner),
Id: config.ID, Id: config.ID,
Description: config.Description, Description: config.Description,
Tls: config.TLS, Tls: config.SMTPConfig.TLS,
Host: config.Host, Host: config.SMTPConfig.Host,
User: config.User, User: config.SMTPConfig.User,
State: settings_pb.SMTPConfigState(config.State), State: SMTPConfigStateToPb(config.State),
SenderAddress: config.SenderAddress, SenderAddress: config.SMTPConfig.SenderAddress,
SenderName: config.SenderName, SenderName: config.SMTPConfig.SenderName,
} }
} }
@ -39,3 +40,16 @@ func SMTPConfigsToPb(configs []*query.SMTPConfig) []*settings_pb.SMTPConfig {
} }
return c return c
} }
func SMTPConfigStateToPb(state domain.SMTPConfigState) settings_pb.SMTPConfigState {
switch state {
case domain.SMTPConfigStateUnspecified, domain.SMTPConfigStateRemoved:
return settings_pb.SMTPConfigState_SMTP_CONFIG_STATE_UNSPECIFIED
case domain.SMTPConfigStateActive:
return settings_pb.SMTPConfigState_SMTP_CONFIG_ACTIVE
case domain.SMTPConfigStateInactive:
return settings_pb.SMTPConfigState_SMTP_CONFIG_INACTIVE
default:
return settings_pb.SMTPConfigState_SMTP_CONFIG_STATE_UNSPECIFIED
}
}

View File

@ -116,7 +116,7 @@ type InstanceSetup struct {
} }
EmailTemplate []byte EmailTemplate []byte
MessageTexts []*domain.CustomMessageText MessageTexts []*domain.CustomMessageText
SMTPConfiguration *smtp.Config SMTPConfiguration *SMTPConfiguration
OIDCSettings *OIDCSettings OIDCSettings *OIDCSettings
Quotas *SetQuotas Quotas *SetQuotas
Features *InstanceFeatures Features *InstanceFeatures
@ -124,6 +124,15 @@ type InstanceSetup struct {
Restrictions *SetRestrictions Restrictions *SetRestrictions
} }
type SMTPConfiguration struct {
Description string
SMTP smtp.SMTP
Tls bool
From string
FromName string
ReplyToAddress string
}
type OIDCSettings struct { type OIDCSettings struct {
AccessTokenLifetime time.Duration AccessTokenLifetime time.Duration
IdTokenLifetime time.Duration IdTokenLifetime time.Duration
@ -440,7 +449,7 @@ func setupOIDCSettings(commands *Commands, validations *[]preparation.Validation
) )
} }
func setupSMTPSettings(commands *Commands, validations *[]preparation.Validation, smtpConfig *smtp.Config, instanceAgg *instance.Aggregate) { func setupSMTPSettings(commands *Commands, validations *[]preparation.Validation, smtpConfig *SMTPConfiguration, instanceAgg *instance.Aggregate) {
if smtpConfig == nil { if smtpConfig == nil {
return return
} }

View File

@ -12,8 +12,20 @@ import (
type IAMSMTPConfigWriteModel struct { type IAMSMTPConfigWriteModel struct {
eventstore.WriteModel eventstore.WriteModel
ID string ID string
Description string Description string
SMTPConfig *SMTPConfig
HTTPConfig *HTTPConfig
State domain.SMTPConfigState
domain string
domainState domain.InstanceDomainState
smtpSenderAddressMatchesInstanceDomain bool
}
type SMTPConfig struct {
TLS bool TLS bool
Host string Host string
User string User string
@ -21,11 +33,6 @@ type IAMSMTPConfigWriteModel struct {
SenderAddress string SenderAddress string
SenderName string SenderName string
ReplyToAddress string ReplyToAddress string
State domain.SMTPConfigState
domain string
domainState domain.InstanceDomainState
smtpSenderAddressMatchesInstanceDomain bool
} }
func NewIAMSMTPConfigWriteModel(instanceID, id, domain string) *IAMSMTPConfigWriteModel { func NewIAMSMTPConfigWriteModel(instanceID, id, domain string) *IAMSMTPConfigWriteModel {
@ -73,6 +80,23 @@ func (wm *IAMSMTPConfigWriteModel) Reduce() error {
continue continue
} }
wm.reduceSMTPConfigChangedEvent(e) wm.reduceSMTPConfigChangedEvent(e)
case *instance.SMTPConfigPasswordChangedEvent:
if wm.ID != e.ID {
continue
}
if e.Password != nil {
wm.SMTPConfig.Password = e.Password
}
case *instance.SMTPConfigHTTPAddedEvent:
if wm.ID != e.ID {
continue
}
wm.reduceSMTPConfigHTTPAddedEvent(e)
case *instance.SMTPConfigHTTPChangedEvent:
if wm.ID != e.ID {
continue
}
wm.reduceSMTPConfigHTTPChangedEvent(e)
case *instance.SMTPConfigRemovedEvent: case *instance.SMTPConfigRemovedEvent:
if wm.ID != e.ID { if wm.ID != e.ID {
continue continue
@ -120,6 +144,8 @@ func (wm *IAMSMTPConfigWriteModel) Query() *eventstore.SearchQueryBuilder {
instance.SMTPConfigRemovedEventType, instance.SMTPConfigRemovedEventType,
instance.SMTPConfigChangedEventType, instance.SMTPConfigChangedEventType,
instance.SMTPConfigPasswordChangedEventType, instance.SMTPConfigPasswordChangedEventType,
instance.SMTPConfigHTTPAddedEventType,
instance.SMTPConfigHTTPChangedEventType,
instance.SMTPConfigActivatedEventType, instance.SMTPConfigActivatedEventType,
instance.SMTPConfigDeactivatedEventType, instance.SMTPConfigDeactivatedEventType,
instance.SMTPConfigRemovedEventType, instance.SMTPConfigRemovedEventType,
@ -133,6 +159,9 @@ func (wm *IAMSMTPConfigWriteModel) Query() *eventstore.SearchQueryBuilder {
func (wm *IAMSMTPConfigWriteModel) NewChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, id, description string, tls bool, fromAddress, fromName, replyToAddress, smtpHost, smtpUser string, smtpPassword *crypto.CryptoValue) (*instance.SMTPConfigChangedEvent, bool, error) { func (wm *IAMSMTPConfigWriteModel) NewChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, id, description string, tls bool, fromAddress, fromName, replyToAddress, smtpHost, smtpUser string, smtpPassword *crypto.CryptoValue) (*instance.SMTPConfigChangedEvent, bool, error) {
changes := make([]instance.SMTPConfigChanges, 0) changes := make([]instance.SMTPConfigChanges, 0)
var err error var err error
if wm.SMTPConfig == nil {
return nil, false, nil
}
if wm.ID != id { if wm.ID != id {
changes = append(changes, instance.ChangeSMTPConfigID(id)) changes = append(changes, instance.ChangeSMTPConfigID(id))
@ -140,22 +169,22 @@ func (wm *IAMSMTPConfigWriteModel) NewChangedEvent(ctx context.Context, aggregat
if wm.Description != description { if wm.Description != description {
changes = append(changes, instance.ChangeSMTPConfigDescription(description)) changes = append(changes, instance.ChangeSMTPConfigDescription(description))
} }
if wm.TLS != tls { if wm.SMTPConfig.TLS != tls {
changes = append(changes, instance.ChangeSMTPConfigTLS(tls)) changes = append(changes, instance.ChangeSMTPConfigTLS(tls))
} }
if wm.SenderAddress != fromAddress { if wm.SMTPConfig.SenderAddress != fromAddress {
changes = append(changes, instance.ChangeSMTPConfigFromAddress(fromAddress)) changes = append(changes, instance.ChangeSMTPConfigFromAddress(fromAddress))
} }
if wm.SenderName != fromName { if wm.SMTPConfig.SenderName != fromName {
changes = append(changes, instance.ChangeSMTPConfigFromName(fromName)) changes = append(changes, instance.ChangeSMTPConfigFromName(fromName))
} }
if wm.ReplyToAddress != replyToAddress { if wm.SMTPConfig.ReplyToAddress != replyToAddress {
changes = append(changes, instance.ChangeSMTPConfigReplyToAddress(replyToAddress)) changes = append(changes, instance.ChangeSMTPConfigReplyToAddress(replyToAddress))
} }
if wm.Host != smtpHost { if wm.SMTPConfig.Host != smtpHost {
changes = append(changes, instance.ChangeSMTPConfigSMTPHost(smtpHost)) changes = append(changes, instance.ChangeSMTPConfigSMTPHost(smtpHost))
} }
if wm.User != smtpUser { if wm.SMTPConfig.User != smtpUser {
changes = append(changes, instance.ChangeSMTPConfigSMTPUser(smtpUser)) changes = append(changes, instance.ChangeSMTPConfigSMTPUser(smtpUser))
} }
if smtpPassword != nil { if smtpPassword != nil {
@ -171,15 +200,58 @@ func (wm *IAMSMTPConfigWriteModel) NewChangedEvent(ctx context.Context, aggregat
return changeEvent, true, nil return changeEvent, true, nil
} }
func (wm *IAMSMTPConfigWriteModel) NewHTTPChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, id, description, endpoint string) (*instance.SMTPConfigHTTPChangedEvent, bool, error) {
changes := make([]instance.SMTPConfigHTTPChanges, 0)
var err error
if wm.HTTPConfig == nil {
return nil, false, nil
}
if wm.ID != id {
changes = append(changes, instance.ChangeSMTPConfigHTTPID(id))
}
if wm.Description != description {
changes = append(changes, instance.ChangeSMTPConfigHTTPDescription(description))
}
if wm.HTTPConfig.Endpoint != endpoint {
changes = append(changes, instance.ChangeSMTPConfigHTTPEndpoint(endpoint))
}
if len(changes) == 0 {
return nil, false, nil
}
changeEvent, err := instance.NewSMTPConfigHTTPChangeEvent(ctx, aggregate, id, changes)
if err != nil {
return nil, false, err
}
return changeEvent, true, nil
}
func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigAddedEvent(e *instance.SMTPConfigAddedEvent) { func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigAddedEvent(e *instance.SMTPConfigAddedEvent) {
wm.Description = e.Description wm.Description = e.Description
wm.TLS = e.TLS wm.SMTPConfig = &SMTPConfig{
wm.Host = e.Host TLS: e.TLS,
wm.User = e.User Host: e.Host,
wm.Password = e.Password User: e.User,
wm.SenderAddress = e.SenderAddress Password: e.Password,
wm.SenderName = e.SenderName SenderName: e.SenderName,
wm.ReplyToAddress = e.ReplyToAddress SenderAddress: e.SenderAddress,
ReplyToAddress: e.ReplyToAddress,
}
wm.State = domain.SMTPConfigStateInactive
// If ID has empty value we're dealing with the old and unique smtp settings
// These would be the default values for ID and State
if e.ID == "" {
wm.Description = "generic"
wm.ID = e.Aggregate().ResourceOwner
wm.State = domain.SMTPConfigStateActive
}
}
func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigHTTPAddedEvent(e *instance.SMTPConfigHTTPAddedEvent) {
wm.Description = e.Description
wm.HTTPConfig = &HTTPConfig{
Endpoint: e.Endpoint,
}
wm.State = domain.SMTPConfigStateInactive wm.State = domain.SMTPConfigStateInactive
// If ID has empty value we're dealing with the old and unique smtp settings // If ID has empty value we're dealing with the old and unique smtp settings
// These would be the default values for ID and State // These would be the default values for ID and State
@ -191,29 +263,54 @@ func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigAddedEvent(e *instance.SMTPCo
} }
func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigChangedEvent(e *instance.SMTPConfigChangedEvent) { func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigChangedEvent(e *instance.SMTPConfigChangedEvent) {
if wm.SMTPConfig == nil {
return
}
if e.Description != nil { if e.Description != nil {
wm.Description = *e.Description wm.Description = *e.Description
} }
if e.TLS != nil { if e.TLS != nil {
wm.TLS = *e.TLS wm.SMTPConfig.TLS = *e.TLS
} }
if e.Host != nil { if e.Host != nil {
wm.Host = *e.Host wm.SMTPConfig.Host = *e.Host
} }
if e.User != nil { if e.User != nil {
wm.User = *e.User wm.SMTPConfig.User = *e.User
} }
if e.Password != nil { if e.Password != nil {
wm.Password = e.Password wm.SMTPConfig.Password = e.Password
} }
if e.FromAddress != nil { if e.FromAddress != nil {
wm.SenderAddress = *e.FromAddress wm.SMTPConfig.SenderAddress = *e.FromAddress
} }
if e.FromName != nil { if e.FromName != nil {
wm.SenderName = *e.FromName wm.SMTPConfig.SenderName = *e.FromName
} }
if e.ReplyToAddress != nil { if e.ReplyToAddress != nil {
wm.ReplyToAddress = *e.ReplyToAddress wm.SMTPConfig.ReplyToAddress = *e.ReplyToAddress
}
// If ID has empty value we're dealing with the old and unique smtp settings
// These would be the default values for ID and State
if e.ID == "" {
wm.Description = "generic"
wm.ID = e.Aggregate().ResourceOwner
wm.State = domain.SMTPConfigStateActive
}
}
func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigHTTPChangedEvent(e *instance.SMTPConfigHTTPChangedEvent) {
if wm.HTTPConfig == nil {
return
}
if e.Description != nil {
wm.Description = *e.Description
}
if e.Endpoint != nil {
wm.HTTPConfig.Endpoint = *e.Endpoint
} }
// If ID has empty value we're dealing with the old and unique smtp settings // If ID has empty value we're dealing with the old and unique smtp settings
@ -227,13 +324,8 @@ func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigChangedEvent(e *instance.SMTP
func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigRemovedEvent(e *instance.SMTPConfigRemovedEvent) { func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigRemovedEvent(e *instance.SMTPConfigRemovedEvent) {
wm.Description = "" wm.Description = ""
wm.TLS = false wm.HTTPConfig = nil
wm.SenderName = "" wm.SMTPConfig = nil
wm.SenderAddress = ""
wm.ReplyToAddress = ""
wm.Host = ""
wm.User = ""
wm.Password = nil
wm.State = domain.SMTPConfigStateRemoved wm.State = domain.SMTPConfigStateRemoved
// If ID has empty value we're dealing with the old and unique smtp settings // If ID has empty value we're dealing with the old and unique smtp settings

View File

@ -15,151 +15,189 @@ import (
"github.com/zitadel/zitadel/internal/zerrors" "github.com/zitadel/zitadel/internal/zerrors"
) )
func (c *Commands) AddSMTPConfig(ctx context.Context, instanceID string, config *smtp.Config) (string, *domain.ObjectDetails, error) { type AddSMTPConfig struct {
id, err := c.idGenerator.Next() Details *domain.ObjectDetails
if err != nil { ResourceOwner string
return "", nil, err ID string
Description string
Host string
User string
Password string
Tls bool
From string
FromName string
ReplyToAddress string
}
func (c *Commands) AddSMTPConfig(ctx context.Context, config *AddSMTPConfig) (err error) {
if config.ResourceOwner == "" {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-PQN0wsqSyi", "Errors.ResourceOwnerMissing")
}
if config.ID == "" {
config.ID, err = c.idGenerator.Next()
if err != nil {
return err
}
} }
from := strings.TrimSpace(config.From) from := strings.TrimSpace(config.From)
if from == "" { if from == "" {
return "", nil, zerrors.ThrowInvalidArgument(nil, "INST-ASv2d", "Errors.Invalid.Argument") return zerrors.ThrowInvalidArgument(nil, "COMMAND-SAAFpV8VKV", "Errors.Invalid.Argument")
} }
fromSplitted := strings.Split(from, "@") fromSplitted := strings.Split(from, "@")
senderDomain := fromSplitted[len(fromSplitted)-1] senderDomain := fromSplitted[len(fromSplitted)-1]
description := strings.TrimSpace(config.Description) description := strings.TrimSpace(config.Description)
replyTo := strings.TrimSpace(config.ReplyToAddress) replyTo := strings.TrimSpace(config.ReplyToAddress)
hostAndPort := strings.TrimSpace(config.SMTP.Host) hostAndPort := strings.TrimSpace(config.Host)
if _, _, err := net.SplitHostPort(hostAndPort); err != nil { if _, _, err := net.SplitHostPort(hostAndPort); err != nil {
return "", nil, zerrors.ThrowInvalidArgument(nil, "INST-9JdRe", "Errors.Invalid.Argument") return zerrors.ThrowInvalidArgument(nil, "COMMAND-EvAtufIinh", "Errors.Invalid.Argument")
} }
var smtpPassword *crypto.CryptoValue var smtpPassword *crypto.CryptoValue
if config.SMTP.Password != "" { if config.Password != "" {
smtpPassword, err = crypto.Encrypt([]byte(config.SMTP.Password), c.smtpEncryption) smtpPassword, err = crypto.Encrypt([]byte(config.Password), c.smtpEncryption)
if err != nil { if err != nil {
return "", nil, err return err
} }
} }
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, senderDomain) smtpConfigWriteModel, err := c.getSMTPConfig(ctx, config.ResourceOwner, config.ID, senderDomain)
if err != nil { if err != nil {
return "", nil, err return err
} }
err = checkSenderAddress(smtpConfigWriteModel) err = checkSenderAddress(smtpConfigWriteModel)
if err != nil { if err != nil {
return "", nil, err return err
} }
iamAgg := InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel) err = c.pushAppendAndReduce(ctx,
pushedEvents, err := c.eventstore.Push(ctx, instance.NewSMTPConfigAddedEvent( smtpConfigWriteModel,
ctx, instance.NewSMTPConfigAddedEvent(
iamAgg, ctx,
id, InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel),
description, config.ID,
config.Tls, description,
config.From, config.Tls,
config.FromName, config.From,
replyTo, config.FromName,
hostAndPort, replyTo,
config.SMTP.User, hostAndPort,
smtpPassword, config.User,
)) smtpPassword,
),
)
if err != nil { if err != nil {
return "", nil, err return err
} }
config.Details = writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel)
err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...) return nil
if err != nil {
return "", nil, err
}
return id, writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
} }
func (c *Commands) ChangeSMTPConfig(ctx context.Context, instanceID string, id string, config *smtp.Config) (*domain.ObjectDetails, error) { type ChangeSMTPConfig struct {
if id == "" { Details *domain.ObjectDetails
return nil, zerrors.ThrowInvalidArgument(nil, "SMTP-x8vo9", "Errors.IDMissing") ResourceOwner string
ID string
Description string
Host string
User string
Password string
Tls bool
From string
FromName string
ReplyToAddress string
}
func (c *Commands) ChangeSMTPConfig(ctx context.Context, config *ChangeSMTPConfig) error {
if config.ResourceOwner == "" {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-jwA8gxldy3", "Errors.ResourceOwnerMissing")
}
if config.ID == "" {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-2JPlSRzuHy", "Errors.IDMissing")
} }
from := strings.TrimSpace(config.From) from := strings.TrimSpace(config.From)
if from == "" { if from == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "INST-HSv2d", "Errors.Invalid.Argument") return zerrors.ThrowInvalidArgument(nil, "COMMAND-gyPUXOTA4N", "Errors.Invalid.Argument")
} }
fromSplitted := strings.Split(from, "@") fromSplitted := strings.Split(from, "@")
senderDomain := fromSplitted[len(fromSplitted)-1] senderDomain := fromSplitted[len(fromSplitted)-1]
description := strings.TrimSpace(config.Description) description := strings.TrimSpace(config.Description)
replyTo := strings.TrimSpace(config.ReplyToAddress) replyTo := strings.TrimSpace(config.ReplyToAddress)
hostAndPort := strings.TrimSpace(config.SMTP.Host) hostAndPort := strings.TrimSpace(config.Host)
if _, _, err := net.SplitHostPort(hostAndPort); err != nil { if _, _, err := net.SplitHostPort(hostAndPort); err != nil {
return nil, zerrors.ThrowInvalidArgument(nil, "INST-Kv875", "Errors.Invalid.Argument") return zerrors.ThrowInvalidArgument(nil, "COMMAND-kZNVkuL32L", "Errors.Invalid.Argument")
} }
var smtpPassword *crypto.CryptoValue var smtpPassword *crypto.CryptoValue
var err error var err error
if config.SMTP.Password != "" { if config.Password != "" {
smtpPassword, err = crypto.Encrypt([]byte(config.SMTP.Password), c.smtpEncryption) smtpPassword, err = crypto.Encrypt([]byte(config.Password), c.smtpEncryption)
if err != nil { if err != nil {
return nil, err return err
} }
} }
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, senderDomain) smtpConfigWriteModel, err := c.getSMTPConfig(ctx, config.ResourceOwner, config.ID, senderDomain)
if err != nil { if err != nil {
return nil, err return err
} }
if !smtpConfigWriteModel.State.Exists() { if !smtpConfigWriteModel.State.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-7j8gv", "Errors.SMTPConfig.NotFound") return zerrors.ThrowNotFound(nil, "COMMAND-j5IDFtt3T1", "Errors.SMTPConfig.NotFound")
} }
err = checkSenderAddress(smtpConfigWriteModel) err = checkSenderAddress(smtpConfigWriteModel)
if err != nil { if err != nil {
return nil, err return err
} }
iamAgg := InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel)
changedEvent, hasChanged, err := smtpConfigWriteModel.NewChangedEvent( changedEvent, hasChanged, err := smtpConfigWriteModel.NewChangedEvent(
ctx, ctx,
iamAgg, InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel),
id, config.ID,
description, description,
config.Tls, config.Tls,
from, from,
config.FromName, config.FromName,
replyTo, replyTo,
hostAndPort, hostAndPort,
config.SMTP.User, config.User,
smtpPassword, smtpPassword,
) )
if err != nil { if err != nil {
return nil, err return err
} }
if !hasChanged { if !hasChanged {
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-lh3op", "Errors.NoChangesFound") config.Details = writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel)
return nil
} }
pushedEvents, err := c.eventstore.Push(ctx, changedEvent) err = c.pushAppendAndReduce(ctx, smtpConfigWriteModel, changedEvent)
if err != nil { if err != nil {
return nil, err return err
} }
err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...) config.Details = writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel)
if err != nil { return nil
return nil, err
}
return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
} }
func (c *Commands) ChangeSMTPConfigPassword(ctx context.Context, instanceID, id string, password string) (*domain.ObjectDetails, error) { func (c *Commands) ChangeSMTPConfigPassword(ctx context.Context, resourceOwner, id string, password string) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) if resourceOwner == "" {
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, "") return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-gHAyvUXCAF", "Errors.ResourceOwnerMissing")
}
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-BCkAf7LcJA", "Errors.IDMissing")
}
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, resourceOwner, id, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
if smtpConfigWriteModel.State != domain.SMTPConfigStateActive { if smtpConfigWriteModel.State != domain.SMTPConfigStateActive {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-3n9ls", "Errors.SMTPConfig.NotFound") return nil, zerrors.ThrowNotFound(nil, "COMMAND-rDHzqjGuKQ", "Errors.SMTPConfig.NotFound")
} }
var smtpPassword *crypto.CryptoValue var smtpPassword *crypto.CryptoValue
@ -170,68 +208,152 @@ func (c *Commands) ChangeSMTPConfigPassword(ctx context.Context, instanceID, id
} }
} }
pushedEvents, err := c.eventstore.Push(ctx, instance.NewSMTPConfigPasswordChangedEvent( err = c.pushAppendAndReduce(ctx,
ctx, smtpConfigWriteModel,
&instanceAgg.Aggregate, instance.NewSMTPConfigPasswordChangedEvent(
id, ctx,
smtpPassword)) InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel),
id,
smtpPassword,
),
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
} }
func (c *Commands) ActivateSMTPConfig(ctx context.Context, instanceID, id, activatedId string) (*domain.ObjectDetails, error) { type AddSMTPConfigHTTP struct {
if id == "" { Details *domain.ObjectDetails
return nil, zerrors.ThrowInvalidArgument(nil, "SMTP-nm56k", "Errors.IDMissing") ResourceOwner string
} ID string
if len(activatedId) > 0 { Description string
_, err := c.DeactivateSMTPConfig(ctx, instanceID, activatedId) Endpoint string
}
func (c *Commands) AddSMTPConfigHTTP(ctx context.Context, config *AddSMTPConfigHTTP) (err error) {
if config.ResourceOwner == "" {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-FTNDXc8ACS", "Errors.ResourceOwnerMissing")
}
if config.ID == "" {
config.ID, err = c.idGenerator.Next()
if err != nil { if err != nil {
return nil, err return err
} }
} }
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, "") smtpConfigWriteModel, err := c.getSMTPConfig(ctx, config.ResourceOwner, config.ID, "")
if err != nil {
return err
}
err = c.pushAppendAndReduce(ctx, smtpConfigWriteModel, instance.NewSMTPConfigHTTPAddedEvent(
ctx,
InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel),
config.ID,
config.Description,
config.Endpoint,
))
if err != nil {
return err
}
config.Details = writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel)
return nil
}
type ChangeSMTPConfigHTTP struct {
Details *domain.ObjectDetails
ResourceOwner string
ID string
Description string
Endpoint string
}
func (c *Commands) ChangeSMTPConfigHTTP(ctx context.Context, config *ChangeSMTPConfigHTTP) (err error) {
if config.ResourceOwner == "" {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-k7QCGOWyJA", "Errors.ResourceOwnerMissing")
}
if config.ID == "" {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-2MHkV8ObWo", "Errors.IDMissing")
}
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, config.ResourceOwner, config.ID, "")
if err != nil {
return err
}
if !smtpConfigWriteModel.State.Exists() || smtpConfigWriteModel.HTTPConfig == nil {
return zerrors.ThrowNotFound(nil, "COMMAND-xIrdledqv4", "Errors.SMTPConfig.NotFound")
}
changedEvent, hasChanged, err := smtpConfigWriteModel.NewHTTPChangedEvent(
ctx,
InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel),
config.ID,
config.Description,
config.Endpoint,
)
if err != nil {
return err
}
if !hasChanged {
config.Details = writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel)
return nil
}
err = c.pushAppendAndReduce(ctx, smtpConfigWriteModel, changedEvent)
if err != nil {
return err
}
config.Details = writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel)
return nil
}
func (c *Commands) ActivateSMTPConfig(ctx context.Context, resourceOwner, id string) (*domain.ObjectDetails, error) {
if resourceOwner == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-h5htMCebv3", "Errors.ResourceOwnerMissing")
}
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-1hPl6oVMJa", "Errors.IDMissing")
}
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, resourceOwner, id, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !smtpConfigWriteModel.State.Exists() { if !smtpConfigWriteModel.State.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-kg8yr", "Errors.SMTPConfig.NotFound") return nil, zerrors.ThrowNotFound(nil, "COMMAND-E9K20hxOS9", "Errors.SMTPConfig.NotFound")
} }
if smtpConfigWriteModel.State == domain.SMTPConfigStateActive { if smtpConfigWriteModel.State == domain.SMTPConfigStateActive {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-ed3lr", "Errors.SMTPConfig.AlreadyActive") return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-vUHBSmBzaw", "Errors.SMTPConfig.AlreadyActive")
} }
iamAgg := InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel) err = c.pushAppendAndReduce(ctx,
pushedEvents, err := c.eventstore.Push(ctx, instance.NewSMTPConfigActivatedEvent( smtpConfigWriteModel,
ctx, instance.NewSMTPConfigActivatedEvent(
iamAgg, ctx,
id)) InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel),
if err != nil { id,
return nil, err ),
} )
err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
} }
func (c *Commands) DeactivateSMTPConfig(ctx context.Context, instanceID, id string) (*domain.ObjectDetails, error) { func (c *Commands) DeactivateSMTPConfig(ctx context.Context, resourceOwner, id string) (*domain.ObjectDetails, error) {
if resourceOwner == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-pvNHou89Tw", "Errors.ResourceOwnerMissing")
}
if id == "" { if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "SMTP-98ikl", "Errors.IDMissing") return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-jLTIMrtApO", "Errors.IDMissing")
} }
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, "") smtpConfigWriteModel, err := c.getSMTPConfig(ctx, resourceOwner, id, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -239,46 +361,47 @@ func (c *Commands) DeactivateSMTPConfig(ctx context.Context, instanceID, id stri
return nil, zerrors.ThrowNotFound(nil, "COMMAND-k39PJ", "Errors.SMTPConfig.NotFound") return nil, zerrors.ThrowNotFound(nil, "COMMAND-k39PJ", "Errors.SMTPConfig.NotFound")
} }
if smtpConfigWriteModel.State == domain.SMTPConfigStateInactive { if smtpConfigWriteModel.State == domain.SMTPConfigStateInactive {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-km8g3", "Errors.SMTPConfig.AlreadyDeactivated") return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-km8g3", "Errors.SMTPConfig.AlreadyDeactivated")
} }
iamAgg := InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel) err = c.pushAppendAndReduce(ctx,
pushedEvents, err := c.eventstore.Push(ctx, instance.NewSMTPConfigDeactivatedEvent( smtpConfigWriteModel,
ctx, instance.NewSMTPConfigDeactivatedEvent(
iamAgg, ctx,
id)) InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel),
if err != nil { id,
return nil, err ),
} )
err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
} }
func (c *Commands) RemoveSMTPConfig(ctx context.Context, instanceID, id string) (*domain.ObjectDetails, error) { func (c *Commands) RemoveSMTPConfig(ctx context.Context, resourceOwner, id string) (*domain.ObjectDetails, error) {
if resourceOwner == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-t2WsPRgGaK", "Errors.ResourceOwnerMissing")
}
if id == "" { if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "SMTP-7f5cv", "Errors.IDMissing") return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-0ZV5whuUfu", "Errors.IDMissing")
} }
smtpConfigWriteModel, err := c.getSMTPConfig(ctx, instanceID, id, "") smtpConfigWriteModel, err := c.getSMTPConfig(ctx, resourceOwner, id, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !smtpConfigWriteModel.State.Exists() { if !smtpConfigWriteModel.State.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-kg8rt", "Errors.SMTPConfig.NotFound") return nil, zerrors.ThrowNotFound(nil, "COMMAND-09CXlTDL6w", "Errors.SMTPConfig.NotFound")
} }
iamAgg := InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel) err = c.pushAppendAndReduce(ctx,
pushedEvents, err := c.eventstore.Push(ctx, instance.NewSMTPConfigRemovedEvent( smtpConfigWriteModel,
ctx, instance.NewSMTPConfigRemovedEvent(
iamAgg, ctx,
id)) InstanceAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel),
if err != nil { id,
return nil, err ),
} )
err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -303,11 +426,11 @@ func (c *Commands) TestSMTPConfig(ctx context.Context, instanceID, id, email str
if err != nil { if err != nil {
return err return err
} }
if !smtpConfigWriteModel.State.Exists() { if !smtpConfigWriteModel.State.Exists() || smtpConfigWriteModel.SMTPConfig == nil {
return zerrors.ThrowNotFound(nil, "SMTP-p9cc", "Errors.SMTPConfig.NotFound") return zerrors.ThrowNotFound(nil, "SMTP-p9cc", "Errors.SMTPConfig.NotFound")
} }
password, err = crypto.DecryptString(smtpConfigWriteModel.Password, c.smtpEncryption) password, err = crypto.DecryptString(smtpConfigWriteModel.SMTPConfig.Password, c.smtpEncryption)
if err != nil { if err != nil {
return err return err
} }
@ -338,23 +461,22 @@ func (c *Commands) TestSMTPConfigById(ctx context.Context, instanceID, id, email
return err return err
} }
if !smtpConfigWriteModel.State.Exists() { if !smtpConfigWriteModel.State.Exists() || smtpConfigWriteModel.SMTPConfig == nil {
return zerrors.ThrowNotFound(nil, "SMTP-99klw", "Errors.SMTPConfig.NotFound") return zerrors.ThrowNotFound(nil, "SMTP-99klw", "Errors.SMTPConfig.NotFound")
} }
password, err := crypto.DecryptString(smtpConfigWriteModel.Password, c.smtpEncryption) password, err := crypto.DecryptString(smtpConfigWriteModel.SMTPConfig.Password, c.smtpEncryption)
if err != nil { if err != nil {
return err return err
} }
smtpConfig := &smtp.Config{ smtpConfig := &smtp.Config{
Description: smtpConfigWriteModel.Description, Tls: smtpConfigWriteModel.SMTPConfig.TLS,
Tls: smtpConfigWriteModel.TLS, From: smtpConfigWriteModel.SMTPConfig.SenderAddress,
From: smtpConfigWriteModel.SenderAddress, FromName: smtpConfigWriteModel.SMTPConfig.SenderName,
FromName: smtpConfigWriteModel.SenderName,
SMTP: smtp.SMTP{ SMTP: smtp.SMTP{
Host: smtpConfigWriteModel.Host, Host: smtpConfigWriteModel.SMTPConfig.Host,
User: smtpConfigWriteModel.User, User: smtpConfigWriteModel.SMTPConfig.User,
Password: password, Password: password,
}, },
} }
@ -373,7 +495,7 @@ func checkSenderAddress(writeModel *IAMSMTPConfigWriteModel) error {
return nil return nil
} }
if !writeModel.domainState.Exists() { if !writeModel.domainState.Exists() {
return zerrors.ThrowInvalidArgument(nil, "INST-83nl8", "Errors.SMTPConfig.SenderAdressNotCustomDomain") return zerrors.ThrowInvalidArgument(nil, "INST-xtWIiR2ZbR", "Errors.SMTPConfig.SenderAdressNotCustomDomain")
} }
return nil return nil
} }

File diff suppressed because it is too large Load Diff

View File

@ -5,8 +5,8 @@ import (
"github.com/zitadel/logging" "github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/notification/channels/email"
"github.com/zitadel/zitadel/internal/notification/channels/sms" "github.com/zitadel/zitadel/internal/notification/channels/sms"
"github.com/zitadel/zitadel/internal/notification/channels/smtp"
"github.com/zitadel/zitadel/internal/notification/channels/webhook" "github.com/zitadel/zitadel/internal/notification/channels/webhook"
"github.com/zitadel/zitadel/internal/notification/handlers" "github.com/zitadel/zitadel/internal/notification/handlers"
"github.com/zitadel/zitadel/internal/notification/senders" "github.com/zitadel/zitadel/internal/notification/senders"
@ -62,20 +62,20 @@ func registerCounter(counter, desc string) {
logging.WithFields("metric", counter).OnError(err).Panic("unable to register counter") logging.WithFields("metric", counter).OnError(err).Panic("unable to register counter")
} }
func (c *channels) Email(ctx context.Context) (*senders.Chain, *smtp.Config, error) { func (c *channels) Email(ctx context.Context) (*senders.Chain, *email.Config, error) {
smtpCfg, err := c.q.GetSMTPConfig(ctx) emailCfg, err := c.q.GetActiveEmailConfig(ctx)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
chain, err := senders.EmailChannels( chain, err := senders.EmailChannels(
ctx, ctx,
smtpCfg, emailCfg,
c.q.GetFileSystemProvider, c.q.GetFileSystemProvider,
c.q.GetLogProvider, c.q.GetLogProvider,
c.counters.success.email, c.counters.success.email,
c.counters.failed.email, c.counters.failed.email,
) )
return chain, smtpCfg, err return chain, emailCfg, err
} }
func (c *channels) SMS(ctx context.Context) (*senders.Chain, *sms.Config, error) { func (c *channels) SMS(ctx context.Context) (*senders.Chain, *sms.Config, error) {

View File

@ -0,0 +1,17 @@
package email
import (
"github.com/zitadel/zitadel/internal/notification/channels/smtp"
"github.com/zitadel/zitadel/internal/notification/channels/webhook"
)
type Config struct {
ProviderConfig *Provider
SMTPConfig *smtp.Config
WebhookConfig *webhook.Config
}
type Provider struct {
ID string `json:"id,omitempty"`
Description string `json:"description,omitempty"`
}

View File

@ -1,7 +1,6 @@
package smtp package smtp
type Config struct { type Config struct {
Description string
SMTP SMTP SMTP SMTP
Tls bool Tls bool
From string From string
@ -18,3 +17,7 @@ type SMTP struct {
func (smtp *SMTP) HasAuth() bool { func (smtp *SMTP) HasAuth() bool {
return smtp.User != "" && smtp.Password != "" return smtp.User != "" && smtp.Password != ""
} }
type ConfigHTTP struct {
Endpoint string
}

View File

@ -0,0 +1,56 @@
package handlers
import (
"context"
"net/http"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto"
"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/zerrors"
)
// GetSMTPConfig 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 {
return nil, err
}
provider := &email.Provider{
ID: config.ID,
Description: config.Description,
}
if config.SMTPConfig != nil {
password, err := crypto.DecryptString(config.SMTPConfig.Password, n.SMTPPasswordCrypto)
if err != nil {
return nil, err
}
return &email.Config{
ProviderConfig: provider,
SMTPConfig: &smtp.Config{
From: config.SMTPConfig.SenderAddress,
FromName: config.SMTPConfig.SenderName,
ReplyToAddress: config.SMTPConfig.ReplyToAddress,
Tls: config.SMTPConfig.TLS,
SMTP: smtp.SMTP{
Host: config.SMTPConfig.Host,
User: config.SMTPConfig.User,
Password: password,
},
},
}, nil
}
if config.HTTPConfig != nil {
return &email.Config{
ProviderConfig: provider,
WebhookConfig: &webhook.Config{
CallURL: config.HTTPConfig.Endpoint,
Method: http.MethodPost,
Headers: nil,
},
}, nil
}
return nil, zerrors.ThrowNotFound(err, "QUERY-KPQleOckOV", "Errors.SMTPConfig.NotFound")
}

View File

@ -1,33 +0,0 @@
package handlers
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/notification/channels/smtp"
)
// GetSMTPConfig reads the iam SMTP provider config
func (n *NotificationQueries) GetSMTPConfig(ctx context.Context) (*smtp.Config, error) {
config, err := n.SMTPConfigActive(ctx, authz.GetInstance(ctx).InstanceID())
if err != nil {
return nil, err
}
password, err := crypto.DecryptString(config.Password, n.SMTPPasswordCrypto)
if err != nil {
return nil, err
}
return &smtp.Config{
Description: config.Description,
From: config.SenderAddress,
FromName: config.SenderName,
ReplyToAddress: config.ReplyToAddress,
Tls: config.TLS,
SMTP: smtp.SMTP{
Host: config.Host,
User: config.User,
Password: password,
},
}, nil
}

View File

@ -15,6 +15,7 @@ import (
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/repository" "github.com/zitadel/zitadel/internal/eventstore/repository"
es_repo_mock "github.com/zitadel/zitadel/internal/eventstore/repository/mock" es_repo_mock "github.com/zitadel/zitadel/internal/eventstore/repository/mock"
"github.com/zitadel/zitadel/internal/notification/channels/email"
channel_mock "github.com/zitadel/zitadel/internal/notification/channels/mock" channel_mock "github.com/zitadel/zitadel/internal/notification/channels/mock"
"github.com/zitadel/zitadel/internal/notification/channels/sms" "github.com/zitadel/zitadel/internal/notification/channels/sms"
"github.com/zitadel/zitadel/internal/notification/channels/smtp" "github.com/zitadel/zitadel/internal/notification/channels/smtp"
@ -1449,7 +1450,27 @@ func newUserNotifier(t *testing.T, ctrl *gomock.Controller, queries *mock.MockQu
f.SMSTokenCrypto, f.SMSTokenCrypto,
), ),
otpEmailTmpl: defaultOTPEmailTemplate, otpEmailTmpl: defaultOTPEmailTemplate,
channels: &channels{Chain: *senders.ChainChannels(channel)}, channels: &channels{
Chain: *senders.ChainChannels(channel),
EmailConfig: &email.Config{
ProviderConfig: &email.Provider{
ID: "ID",
Description: "Description",
},
SMTPConfig: &smtp.Config{
SMTP: smtp.SMTP{
Host: "host",
User: "user",
Password: "password",
},
Tls: true,
From: "from",
FromName: "fromName",
ReplyToAddress: "replyToAddress",
},
WebhookConfig: nil,
},
},
} }
} }
@ -1457,10 +1478,11 @@ var _ types.ChannelChains = (*channels)(nil)
type channels struct { type channels struct {
senders.Chain senders.Chain
EmailConfig *email.Config
} }
func (c *channels) Email(context.Context) (*senders.Chain, *smtp.Config, error) { func (c *channels) Email(context.Context) (*senders.Chain, *email.Config, error) {
return &c.Chain, nil, nil return &c.Chain, c.EmailConfig, nil
} }
func (c *channels) SMS(context.Context) (*senders.Chain, *sms.Config, error) { func (c *channels) SMS(context.Context) (*senders.Chain, *sms.Config, error) {

View File

@ -7,38 +7,61 @@ import (
"github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/notification/channels" "github.com/zitadel/zitadel/internal/notification/channels"
"github.com/zitadel/zitadel/internal/notification/channels/email"
"github.com/zitadel/zitadel/internal/notification/channels/fs" "github.com/zitadel/zitadel/internal/notification/channels/fs"
"github.com/zitadel/zitadel/internal/notification/channels/instrumenting" "github.com/zitadel/zitadel/internal/notification/channels/instrumenting"
"github.com/zitadel/zitadel/internal/notification/channels/log" "github.com/zitadel/zitadel/internal/notification/channels/log"
"github.com/zitadel/zitadel/internal/notification/channels/smtp" "github.com/zitadel/zitadel/internal/notification/channels/smtp"
"github.com/zitadel/zitadel/internal/notification/channels/webhook"
) )
const smtpSpanName = "smtp.NotificationChannel" const smtpSpanName = "smtp.NotificationChannel"
func EmailChannels( func EmailChannels(
ctx context.Context, ctx context.Context,
emailConfig *smtp.Config, emailConfig *email.Config,
getFileSystemProvider func(ctx context.Context) (*fs.Config, error), getFileSystemProvider func(ctx context.Context) (*fs.Config, error),
getLogProvider func(ctx context.Context) (*log.Config, error), getLogProvider func(ctx context.Context) (*log.Config, error),
successMetricName, successMetricName,
failureMetricName string, failureMetricName string,
) (chain *Chain, err error) { ) (chain *Chain, err error) {
channels := make([]channels.NotificationChannel, 0, 3) channels := make([]channels.NotificationChannel, 0, 3)
p, err := smtp.InitChannel(emailConfig) if emailConfig.SMTPConfig != nil {
logging.WithFields( p, err := smtp.InitChannel(emailConfig.SMTPConfig)
"instance", authz.GetInstance(ctx).InstanceID(), logging.WithFields(
).OnError(err).Debug("initializing SMTP channel failed") "instance", authz.GetInstance(ctx).InstanceID(),
if err == nil { ).OnError(err).Debug("initializing SMTP channel failed")
channels = append( if err == nil {
channels, channels = append(
instrumenting.Wrap( channels,
ctx, instrumenting.Wrap(
p, ctx,
smtpSpanName, p,
successMetricName, smtpSpanName,
failureMetricName, successMetricName,
), failureMetricName,
) ),
)
}
}
if emailConfig.WebhookConfig != nil {
webhookChannel, err := webhook.InitChannel(ctx, *emailConfig.WebhookConfig)
logging.WithFields(
"instance", authz.GetInstance(ctx).InstanceID(),
"callurl", emailConfig.WebhookConfig.CallURL,
).OnError(err).Debug("initializing JSON channel failed")
if err == nil {
channels = append(
channels,
instrumenting.Wrap(
ctx,
webhookChannel,
webhookSpanName,
successMetricName,
failureMetricName,
),
)
}
} }
channels = append(channels, debugChannels(ctx, getFileSystemProvider, getLogProvider)...) channels = append(channels, debugChannels(ctx, getFileSystemProvider, getLogProvider)...)
return ChainChannels(channels...), nil return ChainChannels(channels...), nil

View File

@ -7,8 +7,8 @@ import (
"github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/i18n" "github.com/zitadel/zitadel/internal/i18n"
"github.com/zitadel/zitadel/internal/notification/channels/email"
"github.com/zitadel/zitadel/internal/notification/channels/sms" "github.com/zitadel/zitadel/internal/notification/channels/sms"
"github.com/zitadel/zitadel/internal/notification/channels/smtp"
"github.com/zitadel/zitadel/internal/notification/channels/webhook" "github.com/zitadel/zitadel/internal/notification/channels/webhook"
"github.com/zitadel/zitadel/internal/notification/senders" "github.com/zitadel/zitadel/internal/notification/senders"
"github.com/zitadel/zitadel/internal/notification/templates" "github.com/zitadel/zitadel/internal/notification/templates"
@ -23,7 +23,7 @@ type Notify func(
) error ) error
type ChannelChains interface { type ChannelChains interface {
Email(context.Context) (*senders.Chain, *smtp.Config, error) Email(context.Context) (*senders.Chain, *email.Config, error)
SMS(context.Context) (*senders.Chain, *sms.Config, error) SMS(context.Context) (*senders.Chain, *sms.Config, error)
Webhook(context.Context, webhook.Config) (*senders.Chain, error) Webhook(context.Context, webhook.Config) (*senders.Chain, error)
} }
@ -54,8 +54,9 @@ func SendEmail(
ctx, ctx,
channels, channels,
user, user,
data.Subject,
template, template,
data,
args,
allowUnverifiedNotificationChannel, allowUnverifiedNotificationChannel,
triggeringEvent, triggeringEvent,
) )

View File

@ -3,9 +3,13 @@ package types
import ( import (
"context" "context"
"html" "html"
"strings"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/notification/messages" "github.com/zitadel/zitadel/internal/notification/messages"
"github.com/zitadel/zitadel/internal/notification/templates"
"github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/zerrors" "github.com/zitadel/zitadel/internal/zerrors"
) )
@ -14,29 +18,56 @@ func generateEmail(
ctx context.Context, ctx context.Context,
channels ChannelChains, channels ChannelChains,
user *query.NotifyUser, user *query.NotifyUser,
subject, template string,
content string, data templates.TemplateData,
args map[string]interface{},
lastEmail bool, lastEmail bool,
triggeringEvent eventstore.Event, triggeringEvent eventstore.Event,
) error { ) error {
content = html.UnescapeString(content) emailChannels, config, err := channels.Email(ctx)
message := &messages.Email{ logging.OnError(err).Error("could not create email channel")
Recipients: []string{user.VerifiedEmail},
Subject: subject,
Content: content,
TriggeringEvent: triggeringEvent,
}
if lastEmail {
message.Recipients = []string{user.LastEmail}
}
emailChannels, _, err := channels.Email(ctx)
if err != nil {
return err
}
if emailChannels == nil || emailChannels.Len() == 0 { if emailChannels == nil || emailChannels.Len() == 0 {
return zerrors.ThrowPreconditionFailed(nil, "MAIL-83nof", "Errors.Notification.Channels.NotPresent") return zerrors.ThrowPreconditionFailed(nil, "PHONE-w8nfow", "Errors.Notification.Channels.NotPresent")
} }
return emailChannels.HandleMessage(message) recipient := user.VerifiedEmail
if lastEmail {
recipient = user.LastEmail
}
if config.SMTPConfig != nil {
message := &messages.Email{
Recipients: []string{recipient},
Subject: data.Subject,
Content: html.UnescapeString(template),
TriggeringEvent: triggeringEvent,
}
return emailChannels.HandleMessage(message)
}
if config.WebhookConfig != nil {
caseArgs := make(map[string]interface{}, len(args))
for k, v := range args {
caseArgs[strings.ToLower(string(k[0]))+k[1:]] = v
}
contextInfo := map[string]interface{}{
"recipientEmailAddress": recipient,
"eventType": triggeringEvent.Type(),
"provider": config.ProviderConfig,
}
message := &messages.JSON{
Serializable: &serializableData{
ContextInfo: contextInfo,
TemplateData: data,
Args: caseArgs,
},
TriggeringEvent: triggeringEvent,
}
webhookChannels, err := channels.Webhook(ctx, *config.WebhookConfig)
if err != nil {
return err
}
return webhookChannels.HandleMessage(message)
}
return zerrors.ThrowPreconditionFailed(nil, "MAIL-83nof", "Errors.Notification.Channels.NotPresent")
} }
func mapNotifyUserToArgs(user *query.NotifyUser, args map[string]interface{}) map[string]interface{} { func mapNotifyUserToArgs(user *query.NotifyUser, args map[string]interface{}) map[string]interface{} {

View File

@ -8,26 +8,38 @@ import (
old_handler "github.com/zitadel/zitadel/internal/eventstore/handler" old_handler "github.com/zitadel/zitadel/internal/eventstore/handler"
"github.com/zitadel/zitadel/internal/eventstore/handler/v2" "github.com/zitadel/zitadel/internal/eventstore/handler/v2"
"github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/zerrors"
) )
const ( const (
SMTPConfigProjectionTable = "projections.smtp_configs2" SMTPConfigProjectionTable = "projections.smtp_configs3"
SMTPConfigColumnInstanceID = "instance_id" SMTPConfigTable = SMTPConfigProjectionTable + "_" + smtpConfigSMTPTableSuffix
SMTPConfigColumnResourceOwner = "resource_owner" SMTPConfigHTTPTable = SMTPConfigProjectionTable + "_" + smtpConfigHTTPTableSuffix
SMTPConfigColumnID = "id"
SMTPConfigColumnCreationDate = "creation_date" SMTPConfigColumnInstanceID = "instance_id"
SMTPConfigColumnChangeDate = "change_date" SMTPConfigColumnResourceOwner = "resource_owner"
SMTPConfigColumnSequence = "sequence" SMTPConfigColumnAggregateID = "aggregate_id"
SMTPConfigColumnTLS = "tls" SMTPConfigColumnID = "id"
SMTPConfigColumnSenderAddress = "sender_address" SMTPConfigColumnCreationDate = "creation_date"
SMTPConfigColumnSenderName = "sender_name" SMTPConfigColumnChangeDate = "change_date"
SMTPConfigColumnReplyToAddress = "reply_to_address" SMTPConfigColumnSequence = "sequence"
SMTPConfigColumnSMTPHost = "host" SMTPConfigColumnState = "state"
SMTPConfigColumnSMTPUser = "username" SMTPConfigColumnDescription = "description"
SMTPConfigColumnSMTPPassword = "password"
SMTPConfigColumnState = "state" smtpConfigSMTPTableSuffix = "smtp"
SMTPConfigColumnDescription = "description" SMTPConfigSMTPColumnInstanceID = "instance_id"
SMTPConfigSMTPColumnID = "id"
SMTPConfigSMTPColumnTLS = "tls"
SMTPConfigSMTPColumnSenderAddress = "sender_address"
SMTPConfigSMTPColumnSenderName = "sender_name"
SMTPConfigSMTPColumnReplyToAddress = "reply_to_address"
SMTPConfigSMTPColumnHost = "host"
SMTPConfigSMTPColumnUser = "username"
SMTPConfigSMTPColumnPassword = "password"
smtpConfigHTTPTableSuffix = "http"
SMTPConfigHTTPColumnInstanceID = "instance_id"
SMTPConfigHTTPColumnID = "id"
SMTPConfigHTTPColumnEndpoint = "endpoint"
) )
type smtpConfigProjection struct{} type smtpConfigProjection struct{}
@ -41,25 +53,43 @@ func (*smtpConfigProjection) Name() string {
} }
func (*smtpConfigProjection) Init() *old_handler.Check { func (*smtpConfigProjection) Init() *old_handler.Check {
return handler.NewTableCheck( return handler.NewMultiTableCheck(
handler.NewTable([]*handler.InitColumn{ handler.NewTable([]*handler.InitColumn{
handler.NewColumn(SMTPConfigColumnID, handler.ColumnTypeText), handler.NewColumn(SMTPConfigColumnID, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnAggregateID, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnCreationDate, handler.ColumnTypeTimestamp), handler.NewColumn(SMTPConfigColumnCreationDate, handler.ColumnTypeTimestamp),
handler.NewColumn(SMTPConfigColumnChangeDate, handler.ColumnTypeTimestamp), handler.NewColumn(SMTPConfigColumnChangeDate, handler.ColumnTypeTimestamp),
handler.NewColumn(SMTPConfigColumnSequence, handler.ColumnTypeInt64), handler.NewColumn(SMTPConfigColumnSequence, handler.ColumnTypeInt64),
handler.NewColumn(SMTPConfigColumnResourceOwner, handler.ColumnTypeText), handler.NewColumn(SMTPConfigColumnResourceOwner, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnInstanceID, handler.ColumnTypeText), handler.NewColumn(SMTPConfigColumnInstanceID, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnTLS, handler.ColumnTypeBool),
handler.NewColumn(SMTPConfigColumnSenderAddress, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnSenderName, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnReplyToAddress, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnSMTPHost, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnSMTPUser, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnSMTPPassword, handler.ColumnTypeJSONB, handler.Nullable()),
handler.NewColumn(SMTPConfigColumnState, handler.ColumnTypeEnum),
handler.NewColumn(SMTPConfigColumnDescription, handler.ColumnTypeText), handler.NewColumn(SMTPConfigColumnDescription, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigColumnState, handler.ColumnTypeEnum),
}, },
handler.NewPrimaryKey(SMTPConfigColumnInstanceID, SMTPConfigColumnResourceOwner, SMTPConfigColumnID), handler.NewPrimaryKey(SMTPConfigColumnInstanceID, SMTPConfigColumnID),
),
handler.NewSuffixedTable([]*handler.InitColumn{
handler.NewColumn(SMTPConfigSMTPColumnID, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigSMTPColumnInstanceID, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigSMTPColumnTLS, handler.ColumnTypeBool),
handler.NewColumn(SMTPConfigSMTPColumnSenderAddress, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigSMTPColumnSenderName, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigSMTPColumnReplyToAddress, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigSMTPColumnHost, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigSMTPColumnUser, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigSMTPColumnPassword, handler.ColumnTypeJSONB, handler.Nullable()),
},
handler.NewPrimaryKey(SMTPConfigSMTPColumnInstanceID, SMTPConfigSMTPColumnID),
smtpConfigSMTPTableSuffix,
handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()),
),
handler.NewSuffixedTable([]*handler.InitColumn{
handler.NewColumn(SMTPConfigHTTPColumnID, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigHTTPColumnInstanceID, handler.ColumnTypeText),
handler.NewColumn(SMTPConfigHTTPColumnEndpoint, handler.ColumnTypeText),
},
handler.NewPrimaryKey(SMTPConfigHTTPColumnInstanceID, SMTPConfigHTTPColumnID),
smtpConfigHTTPTableSuffix,
handler.WithForeignKey(handler.NewForeignKeyOfPublicKeys()),
), ),
) )
} }
@ -81,6 +111,14 @@ func (p *smtpConfigProjection) Reducers() []handler.AggregateReducer {
Event: instance.SMTPConfigPasswordChangedEventType, Event: instance.SMTPConfigPasswordChangedEventType,
Reduce: p.reduceSMTPConfigPasswordChanged, Reduce: p.reduceSMTPConfigPasswordChanged,
}, },
{
Event: instance.SMTPConfigHTTPAddedEventType,
Reduce: p.reduceSMTPConfigHTTPAdded,
},
{
Event: instance.SMTPConfigHTTPChangedEventType,
Reduce: p.reduceSMTPConfigHTTPChanged,
},
{ {
Event: instance.SMTPConfigActivatedEventType, Event: instance.SMTPConfigActivatedEventType,
Reduce: p.reduceSMTPConfigActivated, Reduce: p.reduceSMTPConfigActivated,
@ -103,9 +141,9 @@ func (p *smtpConfigProjection) Reducers() []handler.AggregateReducer {
} }
func (p *smtpConfigProjection) reduceSMTPConfigAdded(event eventstore.Event) (*handler.Statement, error) { func (p *smtpConfigProjection) reduceSMTPConfigAdded(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMTPConfigAddedEvent) e, err := assertEvent[*instance.SMTPConfigAddedEvent](event)
if !ok { if err != nil {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-sk99F", "reduce.wrong.event.type %s", instance.SMTPConfigAddedEventType) return nil, err
} }
// Deal with old and unique SMTP settings (empty ID) // Deal with old and unique SMTP settings (empty ID)
@ -118,83 +156,116 @@ func (p *smtpConfigProjection) reduceSMTPConfigAdded(event eventstore.Event) (*h
state = domain.SMTPConfigStateActive state = domain.SMTPConfigStateActive
} }
return handler.NewCreateStatement( return handler.NewMultiStatement(
e, e,
[]handler.Column{ handler.AddCreateStatement(
handler.NewCol(SMTPConfigColumnCreationDate, e.CreationDate()), []handler.Column{
handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()), handler.NewCol(SMTPConfigColumnCreationDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner), handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCol(SMTPConfigColumnSequence, e.Sequence()), handler.NewCol(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCol(SMTPConfigColumnID, id), handler.NewCol(SMTPConfigColumnAggregateID, e.Aggregate().ID),
handler.NewCol(SMTPConfigColumnTLS, e.TLS), handler.NewCol(SMTPConfigColumnID, id),
handler.NewCol(SMTPConfigColumnSenderAddress, e.SenderAddress), handler.NewCol(SMTPConfigColumnSequence, e.Sequence()),
handler.NewCol(SMTPConfigColumnSenderName, e.SenderName), handler.NewCol(SMTPConfigColumnState, state),
handler.NewCol(SMTPConfigColumnReplyToAddress, e.ReplyToAddress), handler.NewCol(SMTPConfigColumnDescription, description),
handler.NewCol(SMTPConfigColumnSMTPHost, e.Host), },
handler.NewCol(SMTPConfigColumnSMTPUser, e.User), ),
handler.NewCol(SMTPConfigColumnSMTPPassword, e.Password), handler.AddCreateStatement(
handler.NewCol(SMTPConfigColumnState, state), []handler.Column{
handler.NewCol(SMTPConfigColumnDescription, description), handler.NewCol(SMTPConfigSMTPColumnInstanceID, e.Aggregate().InstanceID),
}, handler.NewCol(SMTPConfigSMTPColumnID, e.ID),
handler.NewCol(SMTPConfigSMTPColumnTLS, e.TLS),
handler.NewCol(SMTPConfigSMTPColumnSenderAddress, e.SenderAddress),
handler.NewCol(SMTPConfigSMTPColumnSenderName, e.SenderName),
handler.NewCol(SMTPConfigSMTPColumnReplyToAddress, e.ReplyToAddress),
handler.NewCol(SMTPConfigSMTPColumnHost, e.Host),
handler.NewCol(SMTPConfigSMTPColumnUser, e.User),
handler.NewCol(SMTPConfigSMTPColumnPassword, e.Password),
},
handler.WithTableSuffix(smtpConfigSMTPTableSuffix),
),
), nil ), nil
} }
func (p *smtpConfigProjection) reduceSMTPConfigChanged(event eventstore.Event) (*handler.Statement, error) { func (p *smtpConfigProjection) reduceSMTPConfigHTTPAdded(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMTPConfigChangedEvent) e, err := assertEvent[*instance.SMTPConfigHTTPAddedEvent](event)
if !ok { if err != nil {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-wl0wd", "reduce.wrong.event.type %s", instance.SMTPConfigChangedEventType) return nil, err
} }
columns := make([]handler.Column, 0, 8) return handler.NewMultiStatement(
columns = append(columns, handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()), e,
handler.NewCol(SMTPConfigColumnSequence, e.Sequence())) handler.AddCreateStatement(
[]handler.Column{
handler.NewCol(SMTPConfigColumnCreationDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCol(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCol(SMTPConfigColumnAggregateID, e.Aggregate().ID),
handler.NewCol(SMTPConfigColumnID, e.ID),
handler.NewCol(SMTPConfigColumnSequence, e.Sequence()),
handler.NewCol(SMTPConfigColumnState, domain.SMTPConfigStateInactive),
handler.NewCol(SMTPConfigColumnDescription, e.Description),
},
),
handler.AddCreateStatement(
[]handler.Column{
handler.NewCol(SMTPConfigSMTPColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCol(SMTPConfigSMTPColumnID, e.ID),
handler.NewCol(SMTPConfigHTTPColumnEndpoint, e.Endpoint),
},
handler.WithTableSuffix(smtpConfigHTTPTableSuffix),
),
), nil
}
// Deal with old and unique SMTP settings (empty ID) func (p *smtpConfigProjection) reduceSMTPConfigHTTPChanged(event eventstore.Event) (*handler.Statement, error) {
id := e.ID e, err := assertEvent[*instance.SMTPConfigHTTPChangedEvent](event)
if e.ID == "" { if err != nil {
id = e.Aggregate().ResourceOwner return nil, err
} }
if e.TLS != nil { stmts := make([]func(eventstore.Event) handler.Exec, 0, 3)
columns = append(columns, handler.NewCol(SMTPConfigColumnTLS, *e.TLS)) columns := []handler.Column{
} handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
if e.FromAddress != nil { handler.NewCol(SMTPConfigColumnSequence, e.Sequence()),
columns = append(columns, handler.NewCol(SMTPConfigColumnSenderAddress, *e.FromAddress))
}
if e.FromName != nil {
columns = append(columns, handler.NewCol(SMTPConfigColumnSenderName, *e.FromName))
}
if e.ReplyToAddress != nil {
columns = append(columns, handler.NewCol(SMTPConfigColumnReplyToAddress, *e.ReplyToAddress))
}
if e.Host != nil {
columns = append(columns, handler.NewCol(SMTPConfigColumnSMTPHost, *e.Host))
}
if e.User != nil {
columns = append(columns, handler.NewCol(SMTPConfigColumnSMTPUser, *e.User))
}
if e.Password != nil {
columns = append(columns, handler.NewCol(SMTPConfigColumnSMTPPassword, *e.Password))
} }
if e.Description != nil { if e.Description != nil {
columns = append(columns, handler.NewCol(SMTPConfigColumnDescription, *e.Description)) columns = append(columns, handler.NewCol(SMTPConfigColumnDescription, *e.Description))
} }
return handler.NewUpdateStatement( if len(columns) > 0 {
e, stmts = append(stmts, handler.AddUpdateStatement(
columns, columns,
[]handler.Condition{ []handler.Condition{
handler.NewCond(SMTPConfigColumnID, id), handler.NewCond(SMTPConfigColumnID, e.ID),
handler.NewCond(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner), handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID), },
}, ))
), nil }
smtpColumns := make([]handler.Column, 0, 1)
if e.Endpoint != nil {
smtpColumns = append(smtpColumns, handler.NewCol(SMTPConfigHTTPColumnEndpoint, *e.Endpoint))
}
if len(smtpColumns) > 0 {
stmts = append(stmts, handler.AddUpdateStatement(
smtpColumns,
[]handler.Condition{
handler.NewCond(SMTPConfigHTTPColumnID, e.ID),
handler.NewCond(SMTPConfigHTTPColumnInstanceID, e.Aggregate().InstanceID),
},
handler.WithTableSuffix(smtpConfigHTTPTableSuffix),
))
}
return handler.NewMultiStatement(e, stmts...), nil
} }
func (p *smtpConfigProjection) reduceSMTPConfigPasswordChanged(event eventstore.Event) (*handler.Statement, error) { func (p *smtpConfigProjection) reduceSMTPConfigChanged(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMTPConfigPasswordChangedEvent) e, err := assertEvent[*instance.SMTPConfigChangedEvent](event)
if !ok { if err != nil {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-fk02f", "reduce.wrong.event.type %s", instance.SMTPConfigChangedEventType) return nil, err
} }
// Deal with old and unique SMTP settings (empty ID) // Deal with old and unique SMTP settings (empty ID)
@ -203,25 +274,101 @@ func (p *smtpConfigProjection) reduceSMTPConfigPasswordChanged(event eventstore.
id = e.Aggregate().ResourceOwner id = e.Aggregate().ResourceOwner
} }
return handler.NewUpdateStatement( stmts := make([]func(eventstore.Event) handler.Exec, 0, 3)
columns := []handler.Column{
handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnSequence, e.Sequence()),
}
if e.Description != nil {
columns = append(columns, handler.NewCol(SMTPConfigColumnDescription, *e.Description))
}
if len(columns) > 0 {
stmts = append(stmts, handler.AddUpdateStatement(
columns,
[]handler.Condition{
handler.NewCond(SMTPConfigColumnID, id),
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
},
))
}
httpColumns := make([]handler.Column, 0, 7)
if e.TLS != nil {
httpColumns = append(httpColumns, handler.NewCol(SMTPConfigSMTPColumnTLS, *e.TLS))
}
if e.FromAddress != nil {
httpColumns = append(httpColumns, handler.NewCol(SMTPConfigSMTPColumnSenderAddress, *e.FromAddress))
}
if e.FromName != nil {
httpColumns = append(httpColumns, handler.NewCol(SMTPConfigSMTPColumnSenderName, *e.FromName))
}
if e.ReplyToAddress != nil {
httpColumns = append(httpColumns, handler.NewCol(SMTPConfigSMTPColumnReplyToAddress, *e.ReplyToAddress))
}
if e.Host != nil {
httpColumns = append(httpColumns, handler.NewCol(SMTPConfigSMTPColumnHost, *e.Host))
}
if e.User != nil {
httpColumns = append(httpColumns, handler.NewCol(SMTPConfigSMTPColumnUser, *e.User))
}
if e.Password != nil {
httpColumns = append(httpColumns, handler.NewCol(SMTPConfigSMTPColumnPassword, *e.Password))
}
if len(httpColumns) > 0 {
stmts = append(stmts, handler.AddUpdateStatement(
httpColumns,
[]handler.Condition{
handler.NewCond(SMTPConfigSMTPColumnID, e.ID),
handler.NewCond(SMTPConfigSMTPColumnInstanceID, e.Aggregate().InstanceID),
},
handler.WithTableSuffix(smtpConfigSMTPTableSuffix),
))
}
return handler.NewMultiStatement(e, stmts...), nil
}
func (p *smtpConfigProjection) reduceSMTPConfigPasswordChanged(event eventstore.Event) (*handler.Statement, error) {
e, err := assertEvent[*instance.SMTPConfigPasswordChangedEvent](event)
if err != nil {
return nil, err
}
// Deal with old and unique SMTP settings (empty ID)
id := e.ID
if e.ID == "" {
id = e.Aggregate().ResourceOwner
}
return handler.NewMultiStatement(
e, e,
[]handler.Column{ handler.AddUpdateStatement(
handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()), []handler.Column{
handler.NewCol(SMTPConfigColumnSequence, e.Sequence()), handler.NewCol(SMTPConfigSMTPColumnPassword, e.Password),
handler.NewCol(SMTPConfigColumnSMTPPassword, e.Password), },
}, []handler.Condition{
[]handler.Condition{ handler.NewCond(SMTPConfigColumnID, id),
handler.NewCond(SMTPConfigColumnID, id), handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCond(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner), },
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID), handler.WithTableSuffix(smtpConfigSMTPTableSuffix),
}, ),
handler.AddUpdateStatement(
[]handler.Column{
handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnSequence, e.Sequence()),
},
[]handler.Condition{
handler.NewCond(SMTPConfigColumnID, id),
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
},
),
), nil ), nil
} }
func (p *smtpConfigProjection) reduceSMTPConfigActivated(event eventstore.Event) (*handler.Statement, error) { func (p *smtpConfigProjection) reduceSMTPConfigActivated(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMTPConfigActivatedEvent) e, err := assertEvent[*instance.SMTPConfigActivatedEvent](event)
if !ok { if err != nil {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-fq92r", "reduce.wrong.event.type %s", instance.SMTPConfigActivatedEventType) return nil, err
} }
// Deal with old and unique SMTP settings (empty ID) // Deal with old and unique SMTP settings (empty ID)
@ -230,25 +377,38 @@ func (p *smtpConfigProjection) reduceSMTPConfigActivated(event eventstore.Event)
id = e.Aggregate().ResourceOwner id = e.Aggregate().ResourceOwner
} }
return handler.NewUpdateStatement( return handler.NewMultiStatement(
e, e,
[]handler.Column{ handler.AddUpdateStatement(
handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()), []handler.Column{
handler.NewCol(SMTPConfigColumnSequence, e.Sequence()), handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnState, domain.SMTPConfigStateActive), handler.NewCol(SMTPConfigColumnSequence, e.Sequence()),
}, handler.NewCol(SMTPConfigColumnState, domain.SMTPConfigStateInactive),
[]handler.Condition{ },
handler.NewCond(SMTPConfigColumnID, id), []handler.Condition{
handler.NewCond(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner), handler.Not(handler.NewCond(SMTPConfigColumnID, e.ID)),
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID), handler.NewCond(SMTPConfigColumnState, domain.SMTPConfigStateActive),
}, handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
},
),
handler.AddUpdateStatement(
[]handler.Column{
handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
handler.NewCol(SMTPConfigColumnSequence, e.Sequence()),
handler.NewCol(SMTPConfigColumnState, domain.SMTPConfigStateActive),
},
[]handler.Condition{
handler.NewCond(SMTPConfigColumnID, id),
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
},
),
), nil ), nil
} }
func (p *smtpConfigProjection) reduceSMTPConfigDeactivated(event eventstore.Event) (*handler.Statement, error) { func (p *smtpConfigProjection) reduceSMTPConfigDeactivated(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMTPConfigDeactivatedEvent) e, err := assertEvent[*instance.SMTPConfigDeactivatedEvent](event)
if !ok { if err != nil {
return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-hv89j", "reduce.wrong.event.type %s", instance.SMTPConfigDeactivatedEventType) return nil, err
} }
// Deal with old and unique SMTP settings (empty ID) // Deal with old and unique SMTP settings (empty ID)
@ -266,7 +426,6 @@ func (p *smtpConfigProjection) reduceSMTPConfigDeactivated(event eventstore.Even
}, },
[]handler.Condition{ []handler.Condition{
handler.NewCond(SMTPConfigColumnID, id), handler.NewCond(SMTPConfigColumnID, id),
handler.NewCond(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID), handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
}, },
), nil ), nil
@ -288,7 +447,6 @@ func (p *smtpConfigProjection) reduceSMTPConfigRemoved(event eventstore.Event) (
e, e,
[]handler.Condition{ []handler.Condition{
handler.NewCond(SMTPConfigColumnID, id), handler.NewCond(SMTPConfigColumnID, id),
handler.NewCond(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID), handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID),
}, },
), nil ), nil

View File

@ -28,19 +28,20 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
instance.SMTPConfigChangedEventType, instance.SMTPConfigChangedEventType,
instance.AggregateType, instance.AggregateType,
[]byte(`{ []byte(`{
"instance_id": "instance-id",
"resource_owner": "ro-id",
"aggregate_id": "agg-id",
"id": "config-id",
"description": "test", "description": "test",
"tls": true, "tls": true,
"senderAddress": "sender", "senderAddress": "sender",
"senderName": "name", "senderName": "name",
"replyToAddress": "reply-to", "replyToAddress": "reply-to",
"host": "host", "host": "host",
"user": "user", "user": "user"
"id": "44444",
"resource_owner": "ro-id",
"instance_id": "instance-id"
}`, }`,
), ),
), instance.SMTPConfigChangedEventMapper), ), eventstore.GenericEventMapper[instance.SMTPConfigChangedEvent]),
}, },
reduce: (&smtpConfigProjection{}).reduceSMTPConfigChanged, reduce: (&smtpConfigProjection{}).reduceSMTPConfigChanged,
want: wantReduce{ want: wantReduce{
@ -49,19 +50,233 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.smtp_configs2 SET (change_date, sequence, tls, sender_address, sender_name, reply_to_address, host, username, description) = ($1, $2, $3, $4, $5, $6, $7, $8, $9) WHERE (id = $10) AND (resource_owner = $11) AND (instance_id = $12)", expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
uint64(15), uint64(15),
"test",
"config-id",
"instance-id",
},
},
{
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)",
expectedArgs: []interface{}{
true, true,
"sender", "sender",
"name", "name",
"reply-to", "reply-to",
"host", "host",
"user", "user",
"config-id",
"instance-id",
},
},
},
},
},
},
{
name: "reduceSMTPConfigChanged, description",
args: args{
event: getEvent(
testEvent(
instance.SMTPConfigChangedEventType,
instance.AggregateType,
[]byte(`{
"instance_id": "instance-id",
"resource_owner": "ro-id",
"aggregate_id": "agg-id",
"id": "config-id",
"description": "test"
}`,
),
), eventstore.GenericEventMapper[instance.SMTPConfigChangedEvent]),
},
reduce: (&smtpConfigProjection{}).reduceSMTPConfigChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"test", "test",
"44444", "config-id",
"ro-id", "instance-id",
},
},
},
},
},
},
{
name: "reduceSMTPConfigChanged, senderAddress",
args: args{
event: getEvent(
testEvent(
instance.SMTPConfigChangedEventType,
instance.AggregateType,
[]byte(`{
"instance_id": "instance-id",
"resource_owner": "ro-id",
"aggregate_id": "agg-id",
"id": "config-id",
"senderAddress": "sender"
}`,
),
), eventstore.GenericEventMapper[instance.SMTPConfigChangedEvent]),
},
reduce: (&smtpConfigProjection{}).reduceSMTPConfigChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"config-id",
"instance-id",
},
},
{
expectedStmt: "UPDATE projections.smtp_configs3_smtp SET sender_address = $1 WHERE (id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
"sender",
"config-id",
"instance-id",
},
},
},
},
},
},
{
name: "reduceSMTPConfigHTTPChanged",
args: args{
event: getEvent(
testEvent(
instance.SMTPConfigHTTPChangedEventType,
instance.AggregateType,
[]byte(`{
"instance_id": "instance-id",
"resource_owner": "ro-id",
"aggregate_id": "agg-id",
"id": "config-id",
"description": "test",
"endpoint": "endpoint"
}`,
),
), eventstore.GenericEventMapper[instance.SMTPConfigHTTPChangedEvent]),
},
reduce: (&smtpConfigProjection{}).reduceSMTPConfigHTTPChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"test",
"config-id",
"instance-id",
},
},
{
expectedStmt: "UPDATE projections.smtp_configs3_http SET endpoint = $1 WHERE (id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
"endpoint",
"config-id",
"instance-id",
},
},
},
},
},
},
{
name: "reduceSMTPConfigHTTPChanged, description",
args: args{
event: getEvent(
testEvent(
instance.SMTPConfigHTTPChangedEventType,
instance.AggregateType,
[]byte(`{
"instance_id": "instance-id",
"resource_owner": "ro-id",
"aggregate_id": "agg-id",
"id": "config-id",
"description": "test"
}`,
),
), eventstore.GenericEventMapper[instance.SMTPConfigHTTPChangedEvent]),
},
reduce: (&smtpConfigProjection{}).reduceSMTPConfigHTTPChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, description) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"test",
"config-id",
"instance-id",
},
},
},
},
},
},
{
name: "reduceSMTPConfigHTTPChanged, endpoint",
args: args{
event: getEvent(
testEvent(
instance.SMTPConfigHTTPChangedEventType,
instance.AggregateType,
[]byte(`{
"instance_id": "instance-id",
"resource_owner": "ro-id",
"aggregate_id": "agg-id",
"id": "config-id",
"endpoint": "endpoint"
}`,
),
), eventstore.GenericEventMapper[instance.SMTPConfigHTTPChangedEvent]),
},
reduce: (&smtpConfigProjection{}).reduceSMTPConfigHTTPChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"config-id",
"instance-id",
},
},
{
expectedStmt: "UPDATE projections.smtp_configs3_http SET endpoint = $1 WHERE (id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
"endpoint",
"config-id",
"instance-id", "instance-id",
}, },
}, },
@ -77,9 +292,12 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
instance.SMTPConfigAddedEventType, instance.SMTPConfigAddedEventType,
instance.AggregateType, instance.AggregateType,
[]byte(`{ []byte(`{
"tls": true, "instance_id": "instance-id",
"id": "id", "resource_owner": "ro-id",
"aggregate_id": "agg-id",
"id": "config-id",
"description": "test", "description": "test",
"tls": true,
"senderAddress": "sender", "senderAddress": "sender",
"senderName": "name", "senderName": "name",
"replyToAddress": "reply-to", "replyToAddress": "reply-to",
@ -91,7 +309,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
"keyId": "key-id" "keyId": "key-id"
} }
}`), }`),
), instance.SMTPConfigAddedEventMapper), ), eventstore.GenericEventMapper[instance.SMTPConfigAddedEvent]),
}, },
reduce: (&smtpConfigProjection{}).reduceSMTPConfigAdded, reduce: (&smtpConfigProjection{}).reduceSMTPConfigAdded,
want: wantReduce{ want: wantReduce{
@ -100,14 +318,24 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "INSERT INTO projections.smtp_configs2 (creation_date, change_date, resource_owner, instance_id, sequence, id, tls, sender_address, sender_name, reply_to_address, host, username, password, state, description) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)", 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)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
anyArg{}, anyArg{},
"ro-id",
"instance-id", "instance-id",
"ro-id",
"agg-id",
"config-id",
uint64(15), uint64(15),
"id", domain.SMTPConfigStateInactive,
"test",
},
},
{
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)",
expectedArgs: []interface{}{
"instance-id",
"config-id",
true, true,
"sender", "sender",
"name", "name",
@ -115,10 +343,58 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
"host", "host",
"user", "user",
anyArg{}, anyArg{},
domain.SMTPConfigState(3), },
},
},
},
},
},
{
name: "reduceSMTPConfigHTTPAdded",
args: args{
event: getEvent(
testEvent(
instance.SMTPConfigHTTPAddedEventType,
instance.AggregateType,
[]byte(`{
"instance_id": "instance-id",
"resource_owner": "ro-id",
"aggregate_id": "agg-id",
"id": "config-id",
"description": "test",
"senderAddress": "sender",
"endpoint": "endpoint"
}`),
), eventstore.GenericEventMapper[instance.SMTPConfigHTTPAddedEvent]),
},
reduce: (&smtpConfigProjection{}).reduceSMTPConfigHTTPAdded,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
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)",
expectedArgs: []interface{}{
anyArg{},
anyArg{},
"instance-id",
"ro-id",
"agg-id",
"config-id",
uint64(15),
domain.SMTPConfigStateInactive,
"test", "test",
}, },
}, },
{
expectedStmt: "INSERT INTO projections.smtp_configs3_http (instance_id, id, endpoint) VALUES ($1, $2, $3)",
expectedArgs: []interface{}{
"instance-id",
"config-id",
"endpoint",
},
},
}, },
}, },
}, },
@ -130,9 +406,12 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
instance.SMTPConfigActivatedEventType, instance.SMTPConfigActivatedEventType,
instance.AggregateType, instance.AggregateType,
[]byte(`{ []byte(`{
"id": "config-id" "instance_id": "instance-id",
"resource_owner": "ro-id",
"aggregate_id": "agg-id",
"id": "config-id"
}`), }`),
), instance.SMTPConfigActivatedEventMapper), ), eventstore.GenericEventMapper[instance.SMTPConfigActivatedEvent]),
}, },
reduce: (&smtpConfigProjection{}).reduceSMTPConfigActivated, reduce: (&smtpConfigProjection{}).reduceSMTPConfigActivated,
want: wantReduce{ want: wantReduce{
@ -141,13 +420,23 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.smtp_configs2 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (resource_owner = $5) AND (instance_id = $6)", expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (NOT (id = $4)) AND (state = $5) AND (instance_id = $6)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
domain.SMTPConfigStateInactive,
"config-id",
domain.SMTPConfigStateActive,
"instance-id",
},
},
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
uint64(15), uint64(15),
domain.SMTPConfigStateActive, domain.SMTPConfigStateActive,
"config-id", "config-id",
"ro-id",
"instance-id", "instance-id",
}, },
}, },
@ -162,9 +451,12 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
instance.SMTPConfigDeactivatedEventType, instance.SMTPConfigDeactivatedEventType,
instance.AggregateType, instance.AggregateType,
[]byte(`{ []byte(`{
"id": "config-id" "instance_id": "instance-id",
"resource_owner": "ro-id",
"aggregate_id": "agg-id",
"id": "config-id"
}`), }`),
), instance.SMTPConfigDeactivatedEventMapper), ), eventstore.GenericEventMapper[instance.SMTPConfigDeactivatedEvent]),
}, },
reduce: (&smtpConfigProjection{}).reduceSMTPConfigDeactivated, reduce: (&smtpConfigProjection{}).reduceSMTPConfigDeactivated,
want: wantReduce{ want: wantReduce{
@ -173,13 +465,12 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.smtp_configs2 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (resource_owner = $5) AND (instance_id = $6)", expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence, state) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
uint64(15), uint64(15),
domain.SMTPConfigStateInactive, domain.SMTPConfigStateInactive,
"config-id", "config-id",
"ro-id",
"instance-id", "instance-id",
}, },
}, },
@ -195,14 +486,17 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
instance.SMTPConfigPasswordChangedEventType, instance.SMTPConfigPasswordChangedEventType,
instance.AggregateType, instance.AggregateType,
[]byte(`{ []byte(`{
"id": "config-id", "instance_id": "instance-id",
"resource_owner": "ro-id",
"aggregate_id": "agg-id",
"id": "config-id",
"password": { "password": {
"cryptoType": 0, "cryptoType": 0,
"algorithm": "RSA-265", "algorithm": "RSA-265",
"keyId": "key-id" "keyId": "key-id"
} }
}`), }`),
), instance.SMTPConfigPasswordChangedEventMapper), ), eventstore.GenericEventMapper[instance.SMTPConfigPasswordChangedEvent]),
}, },
reduce: (&smtpConfigProjection{}).reduceSMTPConfigPasswordChanged, reduce: (&smtpConfigProjection{}).reduceSMTPConfigPasswordChanged,
want: wantReduce{ want: wantReduce{
@ -211,13 +505,19 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "UPDATE projections.smtp_configs2 SET (change_date, sequence, password) = ($1, $2, $3) WHERE (id = $4) AND (resource_owner = $5) AND (instance_id = $6)", expectedStmt: "UPDATE projections.smtp_configs3_smtp SET password = $1 WHERE (id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
anyArg{},
"config-id",
"instance-id",
},
},
{
expectedStmt: "UPDATE projections.smtp_configs3 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
anyArg{}, anyArg{},
uint64(15), uint64(15),
anyArg{},
"config-id", "config-id",
"ro-id",
"instance-id", "instance-id",
}, },
}, },
@ -231,8 +531,13 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
event: getEvent(testEvent( event: getEvent(testEvent(
instance.SMTPConfigRemovedEventType, instance.SMTPConfigRemovedEventType,
instance.AggregateType, instance.AggregateType,
[]byte(`{ "id": "config-id"}`), []byte(`{
), instance.SMTPConfigRemovedEventMapper), "instance_id": "instance-id",
"resource_owner": "ro-id",
"aggregate_id": "agg-id",
"id": "config-id"
}`),
), eventstore.GenericEventMapper[instance.SMTPConfigRemovedEvent]),
}, },
reduce: (&smtpConfigProjection{}).reduceSMTPConfigRemoved, reduce: (&smtpConfigProjection{}).reduceSMTPConfigRemoved,
want: wantReduce{ want: wantReduce{
@ -241,10 +546,9 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "DELETE FROM projections.smtp_configs2 WHERE (id = $1) AND (resource_owner = $2) AND (instance_id = $3)", expectedStmt: "DELETE FROM projections.smtp_configs3 WHERE (id = $1) AND (instance_id = $2)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
"config-id", "config-id",
"ro-id",
"instance-id", "instance-id",
}, },
}, },
@ -269,7 +573,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) {
executer: &testExecuter{ executer: &testExecuter{
executions: []execution{ executions: []execution{
{ {
expectedStmt: "DELETE FROM projections.smtp_configs2 WHERE (instance_id = $1)", expectedStmt: "DELETE FROM projections.smtp_configs3 WHERE (instance_id = $1)",
expectedArgs: []interface{}{ expectedArgs: []interface{}{
"agg-id", "agg-id",
}, },

View File

@ -256,7 +256,7 @@ func prepareSMSConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectBu
&twilioConfig.token, &twilioConfig.token,
&twilioConfig.senderNumber, &twilioConfig.senderNumber,
&httpConfig.smsID, &httpConfig.id,
&httpConfig.endpoint, &httpConfig.endpoint,
) )
@ -268,7 +268,7 @@ func prepareSMSConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectBu
} }
twilioConfig.set(config) twilioConfig.set(config)
httpConfig.set(config) httpConfig.setSMS(config)
return config, nil return config, nil
} }
@ -322,7 +322,7 @@ func prepareSMSConfigsQuery(ctx context.Context, db prepareDatabase) (sq.SelectB
&twilioConfig.token, &twilioConfig.token,
&twilioConfig.senderNumber, &twilioConfig.senderNumber,
&httpConfig.smsID, &httpConfig.id,
&httpConfig.endpoint, &httpConfig.endpoint,
&configs.Count, &configs.Count,
@ -333,7 +333,7 @@ func prepareSMSConfigsQuery(ctx context.Context, db prepareDatabase) (sq.SelectB
} }
twilioConfig.set(config) twilioConfig.set(config)
httpConfig.set(config) httpConfig.setSMS(config)
configs.Configs = append(configs.Configs, config) configs.Configs = append(configs.Configs, config)
} }
@ -361,12 +361,12 @@ func (c sqlTwilioConfig) set(smsConfig *SMSConfig) {
} }
type sqlHTTPConfig struct { type sqlHTTPConfig struct {
smsID sql.NullString id sql.NullString
endpoint sql.NullString endpoint sql.NullString
} }
func (c sqlHTTPConfig) set(smsConfig *SMSConfig) { func (c sqlHTTPConfig) setSMS(smsConfig *SMSConfig) {
if !c.smsID.Valid { if !c.id.Valid {
return return
} }
smsConfig.HTTPConfig = &HTTP{ smsConfig.HTTPConfig = &HTTP{

View File

@ -52,34 +52,6 @@ var (
name: projection.SMTPConfigColumnSequence, name: projection.SMTPConfigColumnSequence,
table: smtpConfigsTable, table: smtpConfigsTable,
} }
SMTPConfigColumnTLS = Column{
name: projection.SMTPConfigColumnTLS,
table: smtpConfigsTable,
}
SMTPConfigColumnSenderAddress = Column{
name: projection.SMTPConfigColumnSenderAddress,
table: smtpConfigsTable,
}
SMTPConfigColumnSenderName = Column{
name: projection.SMTPConfigColumnSenderName,
table: smtpConfigsTable,
}
SMTPConfigColumnReplyToAddress = Column{
name: projection.SMTPConfigColumnReplyToAddress,
table: smtpConfigsTable,
}
SMTPConfigColumnSMTPHost = Column{
name: projection.SMTPConfigColumnSMTPHost,
table: smtpConfigsTable,
}
SMTPConfigColumnSMTPUser = Column{
name: projection.SMTPConfigColumnSMTPUser,
table: smtpConfigsTable,
}
SMTPConfigColumnSMTPPassword = Column{
name: projection.SMTPConfigColumnSMTPPassword,
table: smtpConfigsTable,
}
SMTPConfigColumnID = Column{ SMTPConfigColumnID = Column{
name: projection.SMTPConfigColumnID, name: projection.SMTPConfigColumnID,
table: smtpConfigsTable, table: smtpConfigsTable,
@ -92,13 +64,82 @@ var (
name: projection.SMTPConfigColumnDescription, name: projection.SMTPConfigColumnDescription,
table: smtpConfigsTable, table: smtpConfigsTable,
} }
smtpConfigsSMTPTable = table{
name: projection.SMTPConfigTable,
instanceIDCol: projection.SMTPConfigColumnInstanceID,
}
SMTPConfigSMTPColumnInstanceID = Column{
name: projection.SMTPConfigColumnInstanceID,
table: smtpConfigsSMTPTable,
}
SMTPConfigSMTPColumnID = Column{
name: projection.SMTPConfigColumnID,
table: smtpConfigsSMTPTable,
}
SMTPConfigSMTPColumnTLS = Column{
name: projection.SMTPConfigSMTPColumnTLS,
table: smtpConfigsSMTPTable,
}
SMTPConfigSMTPColumnSenderAddress = Column{
name: projection.SMTPConfigSMTPColumnSenderAddress,
table: smtpConfigsSMTPTable,
}
SMTPConfigSMTPColumnSenderName = Column{
name: projection.SMTPConfigSMTPColumnSenderName,
table: smtpConfigsSMTPTable,
}
SMTPConfigSMTPColumnReplyToAddress = Column{
name: projection.SMTPConfigSMTPColumnReplyToAddress,
table: smtpConfigsSMTPTable,
}
SMTPConfigSMTPColumnHost = Column{
name: projection.SMTPConfigSMTPColumnHost,
table: smtpConfigsSMTPTable,
}
SMTPConfigSMTPColumnUser = Column{
name: projection.SMTPConfigSMTPColumnUser,
table: smtpConfigsSMTPTable,
}
SMTPConfigSMTPColumnPassword = Column{
name: projection.SMTPConfigSMTPColumnPassword,
table: smtpConfigsSMTPTable,
}
smtpConfigsHTTPTable = table{
name: projection.SMTPConfigHTTPTable,
instanceIDCol: projection.SMTPConfigHTTPColumnInstanceID,
}
SMTPConfigHTTPColumnInstanceID = Column{
name: projection.SMTPConfigHTTPColumnInstanceID,
table: smtpConfigsHTTPTable,
}
SMTPConfigHTTPColumnID = Column{
name: projection.SMTPConfigHTTPColumnID,
table: smtpConfigsHTTPTable,
}
SMTPConfigHTTPColumnEndpoint = Column{
name: projection.SMTPConfigHTTPColumnEndpoint,
table: smtpConfigsHTTPTable,
}
) )
type SMTPConfig struct { type SMTPConfig struct {
CreationDate time.Time CreationDate time.Time
ChangeDate time.Time ChangeDate time.Time
ResourceOwner string ResourceOwner string
Sequence uint64 AggregateID string
ID string
Sequence uint64
Description string
SMTPConfig *SMTP
HTTPConfig *HTTP
State domain.SMTPConfigState
}
type SMTP struct {
TLS bool TLS bool
SenderAddress string SenderAddress string
SenderName string SenderName string
@ -106,9 +147,6 @@ type SMTPConfig struct {
Host string Host string
User string User string
Password *crypto.CryptoValue Password *crypto.CryptoValue
ID string
State domain.SMTPConfigState
Description string
} }
func (q *Queries) SMTPConfigActive(ctx context.Context, resourceOwner string) (config *SMTPConfig, err error) { func (q *Queries) SMTPConfigActive(ctx context.Context, resourceOwner string) (config *SMTPConfig, err error) {
@ -132,15 +170,14 @@ func (q *Queries) SMTPConfigActive(ctx context.Context, resourceOwner string) (c
return config, err return config, err
} }
func (q *Queries) SMTPConfigByID(ctx context.Context, instanceID, resourceOwner, id string) (config *SMTPConfig, err error) { func (q *Queries) SMTPConfigByID(ctx context.Context, instanceID, id string) (config *SMTPConfig, err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
stmt, scan := prepareSMTPConfigQuery(ctx, q.client) stmt, scan := prepareSMTPConfigQuery(ctx, q.client)
query, args, err := stmt.Where(sq.Eq{ query, args, err := stmt.Where(sq.Eq{
SMTPConfigColumnResourceOwner.identifier(): resourceOwner, SMTPConfigColumnInstanceID.identifier(): instanceID,
SMTPConfigColumnInstanceID.identifier(): instanceID, SMTPConfigColumnID.identifier(): id,
SMTPConfigColumnID.identifier(): id,
}).ToSql() }).ToSql()
if err != nil { if err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-8f8gw", "Errors.Query.SQLStatement") return nil, zerrors.ThrowInternal(err, "QUERY-8f8gw", "Errors.Query.SQLStatement")
@ -161,35 +198,49 @@ func prepareSMTPConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectB
SMTPConfigColumnChangeDate.identifier(), SMTPConfigColumnChangeDate.identifier(),
SMTPConfigColumnResourceOwner.identifier(), SMTPConfigColumnResourceOwner.identifier(),
SMTPConfigColumnSequence.identifier(), SMTPConfigColumnSequence.identifier(),
SMTPConfigColumnTLS.identifier(),
SMTPConfigColumnSenderAddress.identifier(),
SMTPConfigColumnSenderName.identifier(),
SMTPConfigColumnReplyToAddress.identifier(),
SMTPConfigColumnSMTPHost.identifier(),
SMTPConfigColumnSMTPUser.identifier(),
SMTPConfigColumnSMTPPassword.identifier(),
SMTPConfigColumnID.identifier(), SMTPConfigColumnID.identifier(),
SMTPConfigColumnState.identifier(), SMTPConfigColumnState.identifier(),
SMTPConfigColumnDescription.identifier()). SMTPConfigColumnDescription.identifier(),
From(smtpConfigsTable.identifier() + db.Timetravel(call.Took(ctx))).
SMTPConfigSMTPColumnID.identifier(),
SMTPConfigSMTPColumnTLS.identifier(),
SMTPConfigSMTPColumnSenderAddress.identifier(),
SMTPConfigSMTPColumnSenderName.identifier(),
SMTPConfigSMTPColumnReplyToAddress.identifier(),
SMTPConfigSMTPColumnHost.identifier(),
SMTPConfigSMTPColumnUser.identifier(),
SMTPConfigSMTPColumnPassword.identifier(),
SMTPConfigHTTPColumnID.identifier(),
SMTPConfigHTTPColumnEndpoint.identifier()).
From(smtpConfigsTable.identifier()).
LeftJoin(join(SMTPConfigSMTPColumnID, SMTPConfigColumnID)).
LeftJoin(join(SMTPConfigHTTPColumnID, SMTPConfigColumnID) + db.Timetravel(call.Took(ctx))).
PlaceholderFormat(sq.Dollar), PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*SMTPConfig, error) { func(row *sql.Row) (*SMTPConfig, error) {
config := new(SMTPConfig) config := new(SMTPConfig)
var (
smtpConfig = sqlSmtpConfig{}
httpConfig = sqlHTTPConfig{}
)
err := row.Scan( err := row.Scan(
&config.CreationDate, &config.CreationDate,
&config.ChangeDate, &config.ChangeDate,
&config.ResourceOwner, &config.ResourceOwner,
&config.Sequence, &config.Sequence,
&config.TLS,
&config.SenderAddress,
&config.SenderName,
&config.ReplyToAddress,
&config.Host,
&config.User,
&password,
&config.ID, &config.ID,
&config.State, &config.State,
&config.Description, &config.Description,
&smtpConfig.id,
&smtpConfig.tls,
&smtpConfig.senderAddress,
&smtpConfig.senderName,
&smtpConfig.replyToAddress,
&smtpConfig.host,
&smtpConfig.user,
&password,
&httpConfig.id,
&httpConfig.endpoint,
) )
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {
@ -197,7 +248,9 @@ func prepareSMTPConfigQuery(ctx context.Context, db prepareDatabase) (sq.SelectB
} }
return nil, zerrors.ThrowInternal(err, "QUERY-9k87F", "Errors.Internal") return nil, zerrors.ThrowInternal(err, "QUERY-9k87F", "Errors.Internal")
} }
config.Password = password smtpConfig.password = password
smtpConfig.set(config)
httpConfig.setSMTP(config)
return config, nil return config, nil
} }
} }
@ -208,38 +261,53 @@ func prepareSMTPConfigsQuery(ctx context.Context, db prepareDatabase) (sq.Select
SMTPConfigColumnChangeDate.identifier(), SMTPConfigColumnChangeDate.identifier(),
SMTPConfigColumnResourceOwner.identifier(), SMTPConfigColumnResourceOwner.identifier(),
SMTPConfigColumnSequence.identifier(), SMTPConfigColumnSequence.identifier(),
SMTPConfigColumnTLS.identifier(),
SMTPConfigColumnSenderAddress.identifier(),
SMTPConfigColumnSenderName.identifier(),
SMTPConfigColumnReplyToAddress.identifier(),
SMTPConfigColumnSMTPHost.identifier(),
SMTPConfigColumnSMTPUser.identifier(),
SMTPConfigColumnSMTPPassword.identifier(),
SMTPConfigColumnID.identifier(), SMTPConfigColumnID.identifier(),
SMTPConfigColumnState.identifier(), SMTPConfigColumnState.identifier(),
SMTPConfigColumnDescription.identifier(), SMTPConfigColumnDescription.identifier(),
countColumn.identifier()).
From(smtpConfigsTable.identifier() + db.Timetravel(call.Took(ctx))). SMTPConfigSMTPColumnID.identifier(),
SMTPConfigSMTPColumnTLS.identifier(),
SMTPConfigSMTPColumnSenderAddress.identifier(),
SMTPConfigSMTPColumnSenderName.identifier(),
SMTPConfigSMTPColumnReplyToAddress.identifier(),
SMTPConfigSMTPColumnHost.identifier(),
SMTPConfigSMTPColumnUser.identifier(),
SMTPConfigSMTPColumnPassword.identifier(),
SMTPConfigHTTPColumnID.identifier(),
SMTPConfigHTTPColumnEndpoint.identifier(),
countColumn.identifier(),
).From(smtpConfigsTable.identifier()).
LeftJoin(join(SMTPConfigSMTPColumnID, SMTPConfigColumnID)).
LeftJoin(join(SMTPConfigHTTPColumnID, SMTPConfigColumnID) + db.Timetravel(call.Took(ctx))).
PlaceholderFormat(sq.Dollar), PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) (*SMTPConfigs, error) { func(rows *sql.Rows) (*SMTPConfigs, error) {
configs := &SMTPConfigs{Configs: []*SMTPConfig{}} configs := &SMTPConfigs{Configs: []*SMTPConfig{}}
for rows.Next() { for rows.Next() {
config := new(SMTPConfig) config := new(SMTPConfig)
password := new(crypto.CryptoValue)
var (
smtpConfig = sqlSmtpConfig{}
httpConfig = sqlHTTPConfig{}
)
err := rows.Scan( err := rows.Scan(
&config.CreationDate, &config.CreationDate,
&config.ChangeDate, &config.ChangeDate,
&config.ResourceOwner, &config.ResourceOwner,
&config.Sequence, &config.Sequence,
&config.TLS,
&config.SenderAddress,
&config.SenderName,
&config.ReplyToAddress,
&config.Host,
&config.User,
&config.Password,
&config.ID, &config.ID,
&config.State, &config.State,
&config.Description, &config.Description,
&smtpConfig.id,
&smtpConfig.tls,
&smtpConfig.senderAddress,
&smtpConfig.senderName,
&smtpConfig.replyToAddress,
&smtpConfig.host,
&smtpConfig.user,
&password,
&httpConfig.id,
&httpConfig.endpoint,
&configs.Count, &configs.Count,
) )
if err != nil { if err != nil {
@ -248,6 +316,9 @@ func prepareSMTPConfigsQuery(ctx context.Context, db prepareDatabase) (sq.Select
} }
return nil, zerrors.ThrowInternal(err, "QUERY-9k87F", "Errors.Internal") return nil, zerrors.ThrowInternal(err, "QUERY-9k87F", "Errors.Internal")
} }
smtpConfig.password = password
smtpConfig.set(config)
httpConfig.setSMTP(config)
configs.Configs = append(configs.Configs, config) configs.Configs = append(configs.Configs, config)
} }
return configs, nil return configs, nil
@ -277,3 +348,38 @@ func (q *Queries) SearchSMTPConfigs(ctx context.Context, queries *SMTPConfigsSea
configs.State, err = q.latestState(ctx, smsConfigsTable) configs.State, err = q.latestState(ctx, smsConfigsTable)
return configs, err return configs, err
} }
type sqlSmtpConfig struct {
id sql.NullString
tls sql.NullBool
senderAddress sql.NullString
senderName sql.NullString
replyToAddress sql.NullString
host sql.NullString
user sql.NullString
password *crypto.CryptoValue
}
func (c sqlSmtpConfig) set(smtpConfig *SMTPConfig) {
if !c.id.Valid {
return
}
smtpConfig.SMTPConfig = &SMTP{
TLS: c.tls.Bool,
SenderAddress: c.senderAddress.String,
SenderName: c.senderName.String,
ReplyToAddress: c.replyToAddress.String,
Host: c.host.String,
User: c.user.String,
Password: c.password,
}
}
func (c sqlHTTPConfig) setSMTP(smtpConfig *SMTPConfig) {
if !c.id.Valid {
return
}
smtpConfig.HTTPConfig = &HTTP{
Endpoint: c.endpoint.String,
}
}

View File

@ -14,27 +14,36 @@ import (
) )
var ( var (
prepareSMTPConfigStmt = `SELECT projections.smtp_configs2.creation_date,` + prepareSMTPConfigStmt = `SELECT projections.smtp_configs3.creation_date,` +
` projections.smtp_configs2.change_date,` + ` projections.smtp_configs3.change_date,` +
` projections.smtp_configs2.resource_owner,` + ` projections.smtp_configs3.resource_owner,` +
` projections.smtp_configs2.sequence,` + ` projections.smtp_configs3.sequence,` +
` projections.smtp_configs2.tls,` + ` projections.smtp_configs3.id,` +
` projections.smtp_configs2.sender_address,` + ` projections.smtp_configs3.state,` +
` projections.smtp_configs2.sender_name,` + ` projections.smtp_configs3.description,` +
` projections.smtp_configs2.reply_to_address,` + ` projections.smtp_configs3_smtp.id,` +
` projections.smtp_configs2.host,` + ` projections.smtp_configs3_smtp.tls,` +
` projections.smtp_configs2.username,` + ` projections.smtp_configs3_smtp.sender_address,` +
` projections.smtp_configs2.password,` + ` projections.smtp_configs3_smtp.sender_name,` +
` projections.smtp_configs2.id,` + ` projections.smtp_configs3_smtp.reply_to_address,` +
` projections.smtp_configs2.state,` + ` projections.smtp_configs3_smtp.host,` +
` projections.smtp_configs2.description` + ` projections.smtp_configs3_smtp.username,` +
` FROM projections.smtp_configs2` + ` 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` +
` AS OF SYSTEM TIME '-1 ms'` ` AS OF SYSTEM TIME '-1 ms'`
prepareSMTPConfigCols = []string{ prepareSMTPConfigCols = []string{
"creation_date", "creation_date",
"change_date", "change_date",
"resource_owner", "resource_owner",
"sequence", "sequence",
"id",
"state",
"description",
"id",
"tls", "tls",
"sender_address", "sender_address",
"sender_name", "sender_name",
@ -43,8 +52,7 @@ var (
"smtp_user", "smtp_user",
"smtp_password", "smtp_password",
"id", "id",
"state", "endpoint",
"description",
} }
) )
@ -89,6 +97,10 @@ func Test_SMTPConfigsPrepares(t *testing.T) {
testNow, testNow,
"ro", "ro",
uint64(20211108), uint64(20211108),
"2232323",
domain.SMTPConfigStateActive,
"test",
"2232323",
true, true,
"sender", "sender",
"name", "name",
@ -96,27 +108,69 @@ func Test_SMTPConfigsPrepares(t *testing.T) {
"host", "host",
"user", "user",
&crypto.CryptoValue{}, &crypto.CryptoValue{},
"2232323", nil,
domain.SMTPConfigStateActive, nil,
"test",
}, },
), ),
}, },
object: &SMTPConfig{ object: &SMTPConfig{
CreationDate: testNow, CreationDate: testNow,
ChangeDate: testNow, ChangeDate: testNow,
ResourceOwner: "ro", ResourceOwner: "ro",
Sequence: 20211108, Sequence: 20211108,
TLS: true, SMTPConfig: &SMTP{
SenderAddress: "sender", TLS: true,
SenderName: "name", SenderAddress: "sender",
ReplyToAddress: "reply-to", SenderName: "name",
Host: "host", ReplyToAddress: "reply-to",
User: "user", Host: "host",
Password: &crypto.CryptoValue{}, User: "user",
ID: "2232323", Password: &crypto.CryptoValue{},
State: domain.SMTPConfigStateActive, },
Description: "test", ID: "2232323",
State: domain.SMTPConfigStateActive,
Description: "test",
},
},
{
name: "prepareSMTPConfigQuery found, http",
prepare: prepareSMTPConfigQuery,
want: want{
sqlExpectations: mockQuery(
regexp.QuoteMeta(prepareSMTPConfigStmt),
prepareSMTPConfigCols,
[]driver.Value{
testNow,
testNow,
"ro",
uint64(20211108),
"2232323",
domain.SMTPConfigStateActive,
"test",
nil,
nil,
nil,
nil,
nil,
nil,
nil,
nil,
"2232323",
"endpoint",
},
),
},
object: &SMTPConfig{
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "ro",
Sequence: 20211108,
HTTPConfig: &HTTP{
Endpoint: "endpoint",
},
ID: "2232323",
State: domain.SMTPConfigStateActive,
Description: "test",
}, },
}, },
{ {
@ -131,6 +185,10 @@ func Test_SMTPConfigsPrepares(t *testing.T) {
testNow, testNow,
"ro", "ro",
uint64(20211109), uint64(20211109),
"44442323",
domain.SMTPConfigStateInactive,
"test2",
"44442323",
true, true,
"sender2", "sender2",
"name2", "name2",
@ -138,27 +196,28 @@ func Test_SMTPConfigsPrepares(t *testing.T) {
"host2", "host2",
"user2", "user2",
&crypto.CryptoValue{}, &crypto.CryptoValue{},
"44442323", nil,
domain.SMTPConfigStateInactive, nil,
"test2",
}, },
), ),
}, },
object: &SMTPConfig{ object: &SMTPConfig{
CreationDate: testNow, CreationDate: testNow,
ChangeDate: testNow, ChangeDate: testNow,
ResourceOwner: "ro", ResourceOwner: "ro",
Sequence: 20211109, Sequence: 20211109,
TLS: true, SMTPConfig: &SMTP{
SenderAddress: "sender2", TLS: true,
SenderName: "name2", SenderAddress: "sender2",
ReplyToAddress: "reply-to2", SenderName: "name2",
Host: "host2", ReplyToAddress: "reply-to2",
User: "user2", Host: "host2",
Password: &crypto.CryptoValue{}, User: "user2",
ID: "44442323", Password: &crypto.CryptoValue{},
State: domain.SMTPConfigStateInactive, },
Description: "test2", ID: "44442323",
State: domain.SMTPConfigStateInactive,
Description: "test2",
}, },
}, },
{ {
@ -173,6 +232,10 @@ func Test_SMTPConfigsPrepares(t *testing.T) {
testNow, testNow,
"ro", "ro",
uint64(20211109), uint64(20211109),
"23234444",
domain.SMTPConfigStateInactive,
"test3",
"23234444",
true, true,
"sender3", "sender3",
"name3", "name3",
@ -180,27 +243,28 @@ func Test_SMTPConfigsPrepares(t *testing.T) {
"host3", "host3",
"user3", "user3",
&crypto.CryptoValue{}, &crypto.CryptoValue{},
"23234444", nil,
domain.SMTPConfigStateInactive, nil,
"test3",
}, },
), ),
}, },
object: &SMTPConfig{ object: &SMTPConfig{
CreationDate: testNow, CreationDate: testNow,
ChangeDate: testNow, ChangeDate: testNow,
ResourceOwner: "ro", ResourceOwner: "ro",
Sequence: 20211109, Sequence: 20211109,
TLS: true, SMTPConfig: &SMTP{
SenderAddress: "sender3", TLS: true,
SenderName: "name3", SenderAddress: "sender3",
ReplyToAddress: "reply-to3", SenderName: "name3",
Host: "host3", ReplyToAddress: "reply-to3",
User: "user3", Host: "host3",
Password: &crypto.CryptoValue{}, User: "user3",
ID: "23234444", Password: &crypto.CryptoValue{},
State: domain.SMTPConfigStateInactive, },
Description: "test3", ID: "23234444",
State: domain.SMTPConfigStateInactive,
Description: "test3",
}, },
}, },
{ {

View File

@ -12,12 +12,14 @@ func init() {
eventstore.RegisterFilterEventMapper(AggregateType, SecretGeneratorAddedEventType, SecretGeneratorAddedEventMapper) eventstore.RegisterFilterEventMapper(AggregateType, SecretGeneratorAddedEventType, SecretGeneratorAddedEventMapper)
eventstore.RegisterFilterEventMapper(AggregateType, SecretGeneratorChangedEventType, SecretGeneratorChangedEventMapper) eventstore.RegisterFilterEventMapper(AggregateType, SecretGeneratorChangedEventType, SecretGeneratorChangedEventMapper)
eventstore.RegisterFilterEventMapper(AggregateType, SecretGeneratorRemovedEventType, SecretGeneratorRemovedEventMapper) eventstore.RegisterFilterEventMapper(AggregateType, SecretGeneratorRemovedEventType, SecretGeneratorRemovedEventMapper)
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigAddedEventType, SMTPConfigAddedEventMapper) eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigAddedEventType, eventstore.GenericEventMapper[SMTPConfigAddedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigChangedEventType, SMTPConfigChangedEventMapper) eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigChangedEventType, eventstore.GenericEventMapper[SMTPConfigChangedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigActivatedEventType, SMTPConfigActivatedEventMapper) eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigActivatedEventType, eventstore.GenericEventMapper[SMTPConfigActivatedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigDeactivatedEventType, SMTPConfigDeactivatedEventMapper) eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigDeactivatedEventType, eventstore.GenericEventMapper[SMTPConfigDeactivatedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigPasswordChangedEventType, SMTPConfigPasswordChangedEventMapper) eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigPasswordChangedEventType, eventstore.GenericEventMapper[SMTPConfigPasswordChangedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigRemovedEventType, SMTPConfigRemovedEventMapper) eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigHTTPAddedEventType, eventstore.GenericEventMapper[SMTPConfigHTTPAddedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigHTTPChangedEventType, eventstore.GenericEventMapper[SMTPConfigHTTPChangedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, SMTPConfigRemovedEventType, eventstore.GenericEventMapper[SMTPConfigRemovedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, SMSConfigTwilioAddedEventType, eventstore.GenericEventMapper[SMSConfigTwilioAddedEvent]) eventstore.RegisterFilterEventMapper(AggregateType, SMSConfigTwilioAddedEventType, eventstore.GenericEventMapper[SMSConfigTwilioAddedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, SMSConfigTwilioChangedEventType, eventstore.GenericEventMapper[SMSConfigTwilioChangedEvent]) eventstore.RegisterFilterEventMapper(AggregateType, SMSConfigTwilioChangedEventType, eventstore.GenericEventMapper[SMSConfigTwilioChangedEvent])
eventstore.RegisterFilterEventMapper(AggregateType, SMSConfigTwilioTokenChangedEventType, eventstore.GenericEventMapper[SMSConfigTwilioTokenChangedEvent]) eventstore.RegisterFilterEventMapper(AggregateType, SMSConfigTwilioTokenChangedEventType, eventstore.GenericEventMapper[SMSConfigTwilioTokenChangedEvent])

View File

@ -10,16 +10,19 @@ import (
const ( const (
smtpConfigPrefix = "smtp.config." smtpConfigPrefix = "smtp.config."
httpConfigPrefix = "http."
SMTPConfigAddedEventType = instanceEventTypePrefix + smtpConfigPrefix + "added" SMTPConfigAddedEventType = instanceEventTypePrefix + smtpConfigPrefix + "added"
SMTPConfigChangedEventType = instanceEventTypePrefix + smtpConfigPrefix + "changed" SMTPConfigChangedEventType = instanceEventTypePrefix + smtpConfigPrefix + "changed"
SMTPConfigPasswordChangedEventType = instanceEventTypePrefix + smtpConfigPrefix + "password.changed" SMTPConfigPasswordChangedEventType = instanceEventTypePrefix + smtpConfigPrefix + "password.changed"
SMTPConfigHTTPAddedEventType = instanceEventTypePrefix + smtpConfigPrefix + httpConfigPrefix + "added"
SMTPConfigHTTPChangedEventType = instanceEventTypePrefix + smtpConfigPrefix + httpConfigPrefix + "changed"
SMTPConfigRemovedEventType = instanceEventTypePrefix + smtpConfigPrefix + "removed" SMTPConfigRemovedEventType = instanceEventTypePrefix + smtpConfigPrefix + "removed"
SMTPConfigActivatedEventType = instanceEventTypePrefix + smtpConfigPrefix + "activated" SMTPConfigActivatedEventType = instanceEventTypePrefix + smtpConfigPrefix + "activated"
SMTPConfigDeactivatedEventType = instanceEventTypePrefix + smtpConfigPrefix + "deactivated" SMTPConfigDeactivatedEventType = instanceEventTypePrefix + smtpConfigPrefix + "deactivated"
) )
type SMTPConfigAddedEvent struct { type SMTPConfigAddedEvent struct {
eventstore.BaseEvent `json:"-"` *eventstore.BaseEvent `json:"-"`
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
@ -45,7 +48,7 @@ func NewSMTPConfigAddedEvent(
password *crypto.CryptoValue, password *crypto.CryptoValue,
) *SMTPConfigAddedEvent { ) *SMTPConfigAddedEvent {
return &SMTPConfigAddedEvent{ return &SMTPConfigAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush( BaseEvent: eventstore.NewBaseEventForPush(
ctx, ctx,
aggregate, aggregate,
SMTPConfigAddedEventType, SMTPConfigAddedEventType,
@ -61,6 +64,9 @@ func NewSMTPConfigAddedEvent(
Password: password, Password: password,
} }
} }
func (e *SMTPConfigAddedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *SMTPConfigAddedEvent) Payload() interface{} { func (e *SMTPConfigAddedEvent) Payload() interface{} {
return e return e
@ -70,29 +76,21 @@ func (e *SMTPConfigAddedEvent) UniqueConstraints() []*eventstore.UniqueConstrain
return nil return nil
} }
func SMTPConfigAddedEventMapper(event eventstore.Event) (eventstore.Event, error) { type SMTPConfigChangedEvent struct {
smtpConfigAdded := &SMTPConfigAddedEvent{ *eventstore.BaseEvent `json:"-"`
BaseEvent: *eventstore.BaseEventFromRepo(event), ID string `json:"id,omitempty"`
} Description *string `json:"description,omitempty"`
err := event.Unmarshal(smtpConfigAdded) FromAddress *string `json:"senderAddress,omitempty"`
if err != nil { FromName *string `json:"senderName,omitempty"`
return nil, zerrors.ThrowInternal(err, "IAM-39fks", "unable to unmarshal smtp config added") ReplyToAddress *string `json:"replyToAddress,omitempty"`
} TLS *bool `json:"tls,omitempty"`
Host *string `json:"host,omitempty"`
return smtpConfigAdded, nil User *string `json:"user,omitempty"`
Password *crypto.CryptoValue `json:"password,omitempty"`
} }
type SMTPConfigChangedEvent struct { func (e *SMTPConfigChangedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
eventstore.BaseEvent `json:"-"` e.BaseEvent = event
ID string `json:"id,omitempty"`
Description *string `json:"description,omitempty"`
FromAddress *string `json:"senderAddress,omitempty"`
FromName *string `json:"senderName,omitempty"`
ReplyToAddress *string `json:"replyToAddress,omitempty"`
TLS *bool `json:"tls,omitempty"`
Host *string `json:"host,omitempty"`
User *string `json:"user,omitempty"`
Password *crypto.CryptoValue `json:"password,omitempty"`
} }
func (e *SMTPConfigChangedEvent) Payload() interface{} { func (e *SMTPConfigChangedEvent) Payload() interface{} {
@ -113,7 +111,7 @@ func NewSMTPConfigChangeEvent(
return nil, zerrors.ThrowPreconditionFailed(nil, "IAM-o0pWf", "Errors.NoChangesFound") return nil, zerrors.ThrowPreconditionFailed(nil, "IAM-o0pWf", "Errors.NoChangesFound")
} }
changeEvent := &SMTPConfigChangedEvent{ changeEvent := &SMTPConfigChangedEvent{
BaseEvent: *eventstore.NewBaseEventForPush( BaseEvent: eventstore.NewBaseEventForPush(
ctx, ctx,
aggregate, aggregate,
SMTPConfigChangedEventType, SMTPConfigChangedEventType,
@ -182,23 +180,10 @@ func ChangeSMTPConfigSMTPPassword(password *crypto.CryptoValue) func(event *SMTP
} }
} }
func SMTPConfigChangedEventMapper(event eventstore.Event) (eventstore.Event, error) {
e := &SMTPConfigChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(e)
if err != nil {
return nil, zerrors.ThrowInternal(err, "IAM-m09oo", "unable to unmarshal smtp changed")
}
return e, nil
}
type SMTPConfigPasswordChangedEvent struct { type SMTPConfigPasswordChangedEvent struct {
eventstore.BaseEvent `json:"-"` *eventstore.BaseEvent `json:"-"`
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Password *crypto.CryptoValue `json:"password,omitempty"` Password *crypto.CryptoValue `json:"password,omitempty"`
} }
func NewSMTPConfigPasswordChangedEvent( func NewSMTPConfigPasswordChangedEvent(
@ -208,7 +193,7 @@ func NewSMTPConfigPasswordChangedEvent(
password *crypto.CryptoValue, password *crypto.CryptoValue,
) *SMTPConfigPasswordChangedEvent { ) *SMTPConfigPasswordChangedEvent {
return &SMTPConfigPasswordChangedEvent{ return &SMTPConfigPasswordChangedEvent{
BaseEvent: *eventstore.NewBaseEventForPush( BaseEvent: eventstore.NewBaseEventForPush(
ctx, ctx,
aggregate, aggregate,
SMTPConfigPasswordChangedEventType, SMTPConfigPasswordChangedEventType,
@ -217,6 +202,10 @@ func NewSMTPConfigPasswordChangedEvent(
} }
} }
func (e *SMTPConfigPasswordChangedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *SMTPConfigPasswordChangedEvent) Payload() interface{} { func (e *SMTPConfigPasswordChangedEvent) Payload() interface{} {
return e return e
} }
@ -225,21 +214,109 @@ func (e *SMTPConfigPasswordChangedEvent) UniqueConstraints() []*eventstore.Uniqu
return nil return nil
} }
func SMTPConfigPasswordChangedEventMapper(event eventstore.Event) (eventstore.Event, error) { type SMTPConfigHTTPAddedEvent struct {
smtpConfigPasswordChanged := &SMTPConfigPasswordChangedEvent{ *eventstore.BaseEvent `json:"-"`
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(smtpConfigPasswordChanged)
if err != nil {
return nil, zerrors.ThrowInternal(err, "IAM-99iNF", "unable to unmarshal smtp config password changed")
}
return smtpConfigPasswordChanged, nil ID string `json:"id,omitempty"`
Description string `json:"description,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
}
func NewSMTPConfigHTTPAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id, description string,
endpoint string,
) *SMTPConfigHTTPAddedEvent {
return &SMTPConfigHTTPAddedEvent{
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
SMTPConfigHTTPAddedEventType,
),
ID: id,
Description: description,
Endpoint: endpoint,
}
}
func (e *SMTPConfigHTTPAddedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *SMTPConfigHTTPAddedEvent) Payload() interface{} {
return e
}
func (e *SMTPConfigHTTPAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
type SMTPConfigHTTPChangedEvent struct {
*eventstore.BaseEvent `json:"-"`
ID string `json:"id,omitempty"`
Description *string `json:"description,omitempty"`
Endpoint *string `json:"endpoint,omitempty"`
}
func (e *SMTPConfigHTTPChangedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *SMTPConfigHTTPChangedEvent) Payload() interface{} {
return e
}
func (e *SMTPConfigHTTPChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func NewSMTPConfigHTTPChangeEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
id string,
changes []SMTPConfigHTTPChanges,
) (*SMTPConfigHTTPChangedEvent, error) {
if len(changes) == 0 {
return nil, zerrors.ThrowPreconditionFailed(nil, "IAM-o0pWf", "Errors.NoChangesFound")
}
changeEvent := &SMTPConfigHTTPChangedEvent{
BaseEvent: eventstore.NewBaseEventForPush(
ctx,
aggregate,
SMTPConfigHTTPChangedEventType,
),
ID: id,
}
for _, change := range changes {
change(changeEvent)
}
return changeEvent, nil
}
type SMTPConfigHTTPChanges func(event *SMTPConfigHTTPChangedEvent)
func ChangeSMTPConfigHTTPID(id string) func(event *SMTPConfigHTTPChangedEvent) {
return func(e *SMTPConfigHTTPChangedEvent) {
e.ID = id
}
}
func ChangeSMTPConfigHTTPDescription(description string) func(event *SMTPConfigHTTPChangedEvent) {
return func(e *SMTPConfigHTTPChangedEvent) {
e.Description = &description
}
}
func ChangeSMTPConfigHTTPEndpoint(endpoint string) func(event *SMTPConfigHTTPChangedEvent) {
return func(e *SMTPConfigHTTPChangedEvent) {
e.Endpoint = &endpoint
}
} }
type SMTPConfigActivatedEvent struct { type SMTPConfigActivatedEvent struct {
eventstore.BaseEvent `json:"-"` *eventstore.BaseEvent `json:"-"`
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
} }
func NewSMTPConfigActivatedEvent( func NewSMTPConfigActivatedEvent(
@ -248,7 +325,7 @@ func NewSMTPConfigActivatedEvent(
id string, id string,
) *SMTPConfigActivatedEvent { ) *SMTPConfigActivatedEvent {
return &SMTPConfigActivatedEvent{ return &SMTPConfigActivatedEvent{
BaseEvent: *eventstore.NewBaseEventForPush( BaseEvent: eventstore.NewBaseEventForPush(
ctx, ctx,
aggregate, aggregate,
SMTPConfigActivatedEventType, SMTPConfigActivatedEventType,
@ -257,6 +334,10 @@ func NewSMTPConfigActivatedEvent(
} }
} }
func (e *SMTPConfigActivatedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *SMTPConfigActivatedEvent) Payload() interface{} { func (e *SMTPConfigActivatedEvent) Payload() interface{} {
return e return e
} }
@ -265,21 +346,9 @@ func (e *SMTPConfigActivatedEvent) UniqueConstraints() []*eventstore.UniqueConst
return nil return nil
} }
func SMTPConfigActivatedEventMapper(event eventstore.Event) (eventstore.Event, error) {
smtpConfigActivated := &SMTPConfigActivatedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(smtpConfigActivated)
if err != nil {
return nil, zerrors.ThrowInternal(err, "IAM-KPr5t", "unable to unmarshal smtp config removed")
}
return smtpConfigActivated, nil
}
type SMTPConfigDeactivatedEvent struct { type SMTPConfigDeactivatedEvent struct {
eventstore.BaseEvent `json:"-"` *eventstore.BaseEvent `json:"-"`
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
} }
func NewSMTPConfigDeactivatedEvent( func NewSMTPConfigDeactivatedEvent(
@ -288,7 +357,7 @@ func NewSMTPConfigDeactivatedEvent(
id string, id string,
) *SMTPConfigDeactivatedEvent { ) *SMTPConfigDeactivatedEvent {
return &SMTPConfigDeactivatedEvent{ return &SMTPConfigDeactivatedEvent{
BaseEvent: *eventstore.NewBaseEventForPush( BaseEvent: eventstore.NewBaseEventForPush(
ctx, ctx,
aggregate, aggregate,
SMTPConfigDeactivatedEventType, SMTPConfigDeactivatedEventType,
@ -297,6 +366,10 @@ func NewSMTPConfigDeactivatedEvent(
} }
} }
func (e *SMTPConfigDeactivatedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *SMTPConfigDeactivatedEvent) Payload() interface{} { func (e *SMTPConfigDeactivatedEvent) Payload() interface{} {
return e return e
} }
@ -305,21 +378,9 @@ func (e *SMTPConfigDeactivatedEvent) UniqueConstraints() []*eventstore.UniqueCon
return nil return nil
} }
func SMTPConfigDeactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) {
smtpConfigDeactivated := &SMTPConfigDeactivatedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(smtpConfigDeactivated)
if err != nil {
return nil, zerrors.ThrowInternal(err, "IAM-KPr5t", "unable to unmarshal smtp config removed")
}
return smtpConfigDeactivated, nil
}
type SMTPConfigRemovedEvent struct { type SMTPConfigRemovedEvent struct {
eventstore.BaseEvent `json:"-"` *eventstore.BaseEvent `json:"-"`
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
} }
func NewSMTPConfigRemovedEvent( func NewSMTPConfigRemovedEvent(
@ -328,7 +389,7 @@ func NewSMTPConfigRemovedEvent(
id string, id string,
) *SMTPConfigRemovedEvent { ) *SMTPConfigRemovedEvent {
return &SMTPConfigRemovedEvent{ return &SMTPConfigRemovedEvent{
BaseEvent: *eventstore.NewBaseEventForPush( BaseEvent: eventstore.NewBaseEventForPush(
ctx, ctx,
aggregate, aggregate,
SMTPConfigRemovedEventType, SMTPConfigRemovedEventType,
@ -337,6 +398,9 @@ func NewSMTPConfigRemovedEvent(
} }
} }
func (e *SMTPConfigRemovedEvent) SetBaseEvent(event *eventstore.BaseEvent) {
e.BaseEvent = event
}
func (e *SMTPConfigRemovedEvent) Payload() interface{} { func (e *SMTPConfigRemovedEvent) Payload() interface{} {
return e return e
} }
@ -344,15 +408,3 @@ func (e *SMTPConfigRemovedEvent) Payload() interface{} {
func (e *SMTPConfigRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { func (e *SMTPConfigRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil return nil
} }
func SMTPConfigRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) {
smtpConfigRemoved := &SMTPConfigRemovedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(smtpConfigRemoved)
if err != nil {
return nil, zerrors.ThrowInternal(err, "IAM-DVw1s", "unable to unmarshal smtp config removed")
}
return smtpConfigRemoved, nil
}

View File

@ -1,3 +1,5 @@
package settings package settings
type SMSConfig = isSMSProvider_Config type SMSConfig = isSMSProvider_Config
type EmailConfig = isEmailProvider_Config

View File

@ -143,32 +143,32 @@ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
} }
security_definitions: { security_definitions: {
security: { security: {
key: "BasicAuth"; key: "BasicAuth";
value: { value: {
type: TYPE_BASIC; type: TYPE_BASIC;
} }
} }
security: { security: {
key: "OAuth2"; key: "OAuth2";
value: { value: {
type: TYPE_OAUTH2; type: TYPE_OAUTH2;
flow: FLOW_ACCESS_CODE; flow: FLOW_ACCESS_CODE;
authorization_url: "$CUSTOM-DOMAIN/oauth/v2/authorize"; authorization_url: "$CUSTOM-DOMAIN/oauth/v2/authorize";
token_url: "$CUSTOM-DOMAIN/oauth/v2/token"; token_url: "$CUSTOM-DOMAIN/oauth/v2/token";
scopes: { scopes: {
scope: { scope: {
key: "openid"; key: "openid";
value: "openid"; value: "openid";
} }
scope: { scope: {
key: "urn:zitadel:iam:org:project:id:zitadel:aud"; key: "urn:zitadel:iam:org:project:id:zitadel:aud";
value: "urn:zitadel:iam:org:project:id:zitadel:aud"; value: "urn:zitadel:iam:org:project:id:zitadel:aud";
} }
} }
} }
} }
} }
security: { security: {
security_requirement: { security_requirement: {
key: "OAuth2"; key: "OAuth2";
@ -422,6 +422,11 @@ service AdminService {
}; };
} }
// Deprecated: Get active SMTP Configuration
//
// Returns the active SMTP configuration from the system. This is used to send E-Mails to the users.
//
// Deprecated: please move to the new endpoint GetEmailProvider.
rpc GetSMTPConfig(GetSMTPConfigRequest) returns (GetSMTPConfigResponse) { rpc GetSMTPConfig(GetSMTPConfigRequest) returns (GetSMTPConfigResponse) {
option (google.api.http) = { option (google.api.http) = {
get: "/smtp"; get: "/smtp";
@ -433,11 +438,15 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP"; tags: "SMTP";
summary: "Get active SMTP Configuration"; deprecated: true;
description: "Returns the active SMTP configuration from the system. This is used to send E-Mails to the users."
}; };
} }
// Deprecated: Get SMTP provider configuration by its id
//
// Get a specific SMTP provider configuration by its ID.
//
// Deprecated: please move to the new endpoint GetEmailProviderById.
rpc GetSMTPConfigById(GetSMTPConfigByIdRequest) returns (GetSMTPConfigByIdResponse) { rpc GetSMTPConfigById(GetSMTPConfigByIdRequest) returns (GetSMTPConfigByIdResponse) {
option (google.api.http) = { option (google.api.http) = {
get: "/smtp/{id}"; get: "/smtp/{id}";
@ -449,11 +458,15 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP"; tags: "SMTP";
summary: "Get SMTP provider configuration by its id"; deprecated: true;
description: "Get a specific SMTP provider configuration by its ID.";
}; };
} }
// Deprecated: Add SMTP Configuration
//
// Add a new SMTP configuration if nothing is set yet.
//
// Deprecated: please move to the new endpoint AddEmailProviderSMTP.
rpc AddSMTPConfig(AddSMTPConfigRequest) returns (AddSMTPConfigResponse) { rpc AddSMTPConfig(AddSMTPConfigRequest) returns (AddSMTPConfigResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/smtp"; post: "/smtp";
@ -466,11 +479,15 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP"; tags: "SMTP";
summary: "Add SMTP Configuration"; deprecated: true;
description: "Add a new SMTP configuration if nothing is set yet."
}; };
} }
// Deprecated: Update SMTP Configuration
//
// Update the SMTP configuration, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured SMTP.
//
// Deprecated: please move to the new endpoint UpdateEmailProviderSMTP.
rpc UpdateSMTPConfig(UpdateSMTPConfigRequest) returns (UpdateSMTPConfigResponse) { rpc UpdateSMTPConfig(UpdateSMTPConfigRequest) returns (UpdateSMTPConfigResponse) {
option (google.api.http) = { option (google.api.http) = {
put: "/smtp/{id}"; put: "/smtp/{id}";
@ -483,11 +500,15 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP"; tags: "SMTP";
summary: "Update SMTP Configuration"; deprecated: true;
description: "Update the SMTP configuration, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured SMTP."
}; };
} }
// Deprecated: Update SMTP Password
//
// Update the SMTP password that is used for the host, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured SMTP.
//
// Deprecated: please move to the new endpoint UpdateEmailProviderSMTPPassword.
rpc UpdateSMTPConfigPassword(UpdateSMTPConfigPasswordRequest) returns (UpdateSMTPConfigPasswordResponse) { rpc UpdateSMTPConfigPassword(UpdateSMTPConfigPasswordRequest) returns (UpdateSMTPConfigPasswordResponse) {
option (google.api.http) = { option (google.api.http) = {
put: "/smtp/{id}/password"; put: "/smtp/{id}/password";
@ -500,11 +521,15 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP"; tags: "SMTP";
summary: "Update SMTP Password"; deprecated: true;
description: "Update the SMTP password that is used for the host, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured SMTP."
}; };
} }
// Deprecated: Activate SMTP Provider
//
// Activate an SMTP provider.
//
// Deprecated: please move to the new endpoint ActivateEmailProvider.
rpc ActivateSMTPConfig(ActivateSMTPConfigRequest) returns (ActivateSMTPConfigResponse) { rpc ActivateSMTPConfig(ActivateSMTPConfigRequest) returns (ActivateSMTPConfigResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/smtp/{id}/_activate"; post: "/smtp/{id}/_activate";
@ -519,9 +544,15 @@ service AdminService {
tags: "SMTP Provider"; tags: "SMTP Provider";
summary: "Activate SMTP Provider"; summary: "Activate SMTP Provider";
description: "Activate an SMTP provider." description: "Activate an SMTP provider."
deprecated: true;
}; };
} }
// Deprecated: Deactivate SMTP Provider
//
// Deactivate an SMTP provider. After deactivating the provider, the users will not be able to receive SMTP notifications from that provider anymore.
//
// Deprecated: please move to the new endpoint DeactivateEmailProvider.
rpc DeactivateSMTPConfig(DeactivateSMTPConfigRequest) returns (DeactivateSMTPConfigResponse) { rpc DeactivateSMTPConfig(DeactivateSMTPConfigRequest) returns (DeactivateSMTPConfigResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/smtp/{id}/_deactivate"; post: "/smtp/{id}/_deactivate";
@ -534,11 +565,15 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP Provider"; tags: "SMTP Provider";
summary: "Deactivate SMTP Provider"; deprecated: true;
description: "Deactivate an SMTP provider. After deactivating the provider, the users will not be able to receive SMTP notifications from that provider anymore."
}; };
} }
// Deprecated: Remove SMTP Configuration
//
// Remove the SMTP configuration, be aware that the users will not get an E-Mail if no SMTP is set.
//
// Deprecated: please move to the new endpoint RemoveEmailProvider.
rpc RemoveSMTPConfig(RemoveSMTPConfigRequest) returns (RemoveSMTPConfigResponse) { rpc RemoveSMTPConfig(RemoveSMTPConfigRequest) returns (RemoveSMTPConfigResponse) {
option (google.api.http) = { option (google.api.http) = {
delete: "/smtp/{id}"; delete: "/smtp/{id}";
@ -550,11 +585,15 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP"; tags: "SMTP";
summary: "Remove SMTP Configuration"; deprecated: true;
description: "Remove the SMTP configuration, be aware that the users will not get an E-Mail if no SMTP is set."
}; };
} }
// Deprecated: Test SMTP Provider
//
// Test an SMTP provider identified by its ID. After testing the provider, the users will receive information about the test results.
//
// Deprecated: please move to the new endpoint TestEmailProviderSMTPById.
rpc TestSMTPConfigById(TestSMTPConfigByIdRequest) returns (TestSMTPConfigByIdResponse) { rpc TestSMTPConfigById(TestSMTPConfigByIdRequest) returns (TestSMTPConfigByIdResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/smtp/{id}/_test"; post: "/smtp/{id}/_test";
@ -567,11 +606,15 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP Provider"; tags: "SMTP Provider";
summary: "Test SMTP Provider "; deprecated: true;
description: "Test an SMTP provider identified by its ID. After testing the provider, the users will receive information about the test results."
}; };
} }
// Deprecated: Test SMTP Provider
//
// Test an SMTP provider. After testing the provider, the users will receive information about the test results.
//
// Deprecated: please move to the new endpoint TestEmailProviderSMTP.
rpc TestSMTPConfig(TestSMTPConfigRequest) returns (TestSMTPConfigResponse) { rpc TestSMTPConfig(TestSMTPConfigRequest) returns (TestSMTPConfigResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/smtp/_test"; post: "/smtp/_test";
@ -584,11 +627,15 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP Provider"; tags: "SMTP Provider";
summary: "Test SMTP Provider"; deprecated: true;
description: "Test an SMTP provider. After testing the provider, the users will receive information about the test results."
}; };
} }
// Deprecated: List SMTP Configs
//
// Returns a list of SMTP configurations.
//
// Deprecated: please move to the new endpoint ListEmailProviders.
rpc ListSMTPConfigs(ListSMTPConfigsRequest) returns (ListSMTPConfigsResponse) { rpc ListSMTPConfigs(ListSMTPConfigsRequest) returns (ListSMTPConfigsResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/smtp/_search" post: "/smtp/_search"
@ -601,8 +648,225 @@ service AdminService {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP Configs"; tags: "SMTP Configs";
summary: "List SMTP Configs"; deprecated: true;
description: "Returns a list of SMTP configurations." };
}
rpc ListEmailProviders(ListEmailProvidersRequest) returns (ListEmailProvidersResponse) {
option (google.api.http) = {
post: "/email/_search"
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.read";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "Email providers";
summary: "List Email providers";
description: "Returns a list of Email providers."
};
}
rpc GetEmailProvider(GetEmailProviderRequest) returns (GetEmailProviderResponse) {
option (google.api.http) = {
get: "/email";
};
option (zitadel.v1.auth_option) = {
permission: "iam.read";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "Email";
summary: "Get active Email provider";
description: "Returns the active Email provider from the system. This is used to send E-Mails to the users."
};
}
rpc GetEmailProviderById(GetEmailProviderByIdRequest) returns (GetEmailProviderByIdResponse) {
option (google.api.http) = {
get: "/email/{id}";
};
option (zitadel.v1.auth_option) = {
permission: "iam.read";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "Email";
summary: "Get Email provider by its id";
description: "Get a specific Email provider by its ID.";
};
}
rpc AddEmailProviderSMTP(AddEmailProviderSMTPRequest) returns (AddEmailProviderSMTPResponse) {
option (google.api.http) = {
post: "/email/smtp";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "Email";
summary: "Add SMTP Email provider";
description: "Add a new SMTP Email provider if nothing is set yet."
};
}
rpc UpdateEmailProviderSMTP(UpdateEmailProviderSMTPRequest) returns (UpdateEmailProviderSMTPResponse) {
option (google.api.http) = {
put: "/email/smtp/{id}";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "Email";
summary: "Update SMTP Email provider";
description: "Update the SMTP Email provider, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured SMTP."
};
}
rpc AddEmailProviderHTTP(AddEmailProviderHTTPRequest) returns (AddEmailProviderHTTPResponse) {
option (google.api.http) = {
post: "/email/http";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "Email";
summary: "Add HTTP Email provider";
description: "Add a new HTTP Email provider if nothing is set yet."
};
}
rpc UpdateEmailProviderHTTP(UpdateEmailProviderHTTPRequest) returns (UpdateEmailProviderHTTPResponse) {
option (google.api.http) = {
put: "/email/http/{id}";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "Email";
summary: "Update HTTP Email provider";
description: "Update the HTTP Email provider, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured HTTP."
};
}
rpc UpdateEmailProviderSMTPPassword(UpdateEmailProviderSMTPPasswordRequest) returns (UpdateEmailProviderSMTPPasswordResponse) {
option (google.api.http) = {
put: "/email/smtp/{id}/password";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP";
summary: "Update SMTP Password";
description: "Update the SMTP password that is used for the host, be aware that this will be activated as soon as it is saved. So the users will get notifications from the newly configured SMTP."
};
}
rpc ActivateEmailProvider(ActivateEmailProviderRequest) returns (ActivateEmailProviderResponse) {
option (google.api.http) = {
post: "/email/{id}/_activate";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "Email Provider";
summary: "Activate Email Provider";
description: "Activate an Email provider."
};
}
rpc DeactivateEmailProvider(DeactivateEmailProviderRequest) returns (DeactivateEmailProviderResponse) {
option (google.api.http) = {
post: "/email/{id}/_deactivate";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "Email Provider";
summary: "Deactivate Email Provider";
description: "Deactivate an Email provider. After deactivating the provider, the users will not be able to receive Email notifications from that provider anymore."
};
}
rpc RemoveEmailProvider(RemoveEmailProviderRequest) returns (RemoveEmailProviderResponse) {
option (google.api.http) = {
delete: "/email/{id}";
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "Email";
summary: "Remove Email provider";
description: "Remove the Email provider, be aware that the users will not get an E-Mail if no provider is set."
};
}
rpc TestEmailProviderSMTPById(TestEmailProviderSMTPByIdRequest) returns (TestEmailProviderSMTPByIdResponse) {
option (google.api.http) = {
post: "/email/smtp/{id}/_test";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP Email Provider";
summary: "Test SMTP Email Provider";
description: "Test an SMTP Email provider identified by its ID. After testing the provider, the users will receive information about the test results."
};
}
rpc TestEmailProviderSMTP(TestEmailProviderSMTPRequest) returns (TestEmailProviderSMTPResponse) {
option (google.api.http) = {
post: "/email/smtp/_test";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMTP EMailProvider";
summary: "Test SMTP Email Provider";
description: "Test an SMTP Email provider. After testing the provider, the users will receive information about the test results."
}; };
} }
@ -690,39 +954,39 @@ service AdminService {
}; };
} }
rpc AddSMSProviderHTTP(AddSMSProviderHTTPRequest) returns (AddSMSProviderHTTPResponse) { rpc AddSMSProviderHTTP(AddSMSProviderHTTPRequest) returns (AddSMSProviderHTTPResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/sms/http"; post: "/sms/http";
body: "*" body: "*"
}; };
option (zitadel.v1.auth_option) = { option (zitadel.v1.auth_option) = {
permission: "iam.write"; permission: "iam.write";
}; };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMS Provider"; tags: "SMS Provider";
summary: "Add HTTP SMS Provider"; summary: "Add HTTP SMS Provider";
description: "Configure a new SMS provider of the type HTTP. A provider has to be activated to be able to send notifications." description: "Configure a new SMS provider of the type HTTP. A provider has to be activated to be able to send notifications."
}; };
} }
rpc UpdateSMSProviderHTTP(UpdateSMSProviderHTTPRequest) returns (UpdateSMSProviderHTTPResponse) { rpc UpdateSMSProviderHTTP(UpdateSMSProviderHTTPRequest) returns (UpdateSMSProviderHTTPResponse) {
option (google.api.http) = { option (google.api.http) = {
put: "/sms/http/{id}"; put: "/sms/http/{id}";
body: "*" body: "*"
}; };
option (zitadel.v1.auth_option) = { option (zitadel.v1.auth_option) = {
permission: "iam.write"; permission: "iam.write";
}; };
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
tags: "SMS Provider"; tags: "SMS Provider";
summary: "Update HTTP SMS Provider"; summary: "Update HTTP SMS Provider";
description: "Change the configuration of an SMS provider of the type HTTP. A provider has to be activated to be able to send notifications." description: "Change the configuration of an SMS provider of the type HTTP. A provider has to be activated to be able to send notifications."
}; };
} }
rpc ActivateSMSProvider(ActivateSMSProviderRequest) returns (ActivateSMSProviderResponse) { rpc ActivateSMSProvider(ActivateSMSProviderRequest) returns (ActivateSMSProviderResponse) {
option (google.api.http) = { option (google.api.http) = {
@ -4561,6 +4825,320 @@ message TestSMTPConfigRequest {
// This is an empty response // This is an empty response
message TestSMTPConfigResponse {} message TestSMTPConfigResponse {}
//This is an empty request
message GetEmailProviderRequest {}
message GetEmailProviderResponse {
zitadel.settings.v1.EmailProvider config = 1;
}
message GetEmailProviderByIdRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 100}];
}
message GetEmailProviderByIdResponse {
zitadel.settings.v1.EmailProvider config = 1;
}
message ListEmailProvidersRequest {
zitadel.v1.ListQuery query = 1;
}
message ListEmailProvidersResponse {
zitadel.v1.ListDetails details = 1;
repeated zitadel.settings.v1.EmailProvider result = 2;
}
message AddEmailProviderSMTPRequest {
string sender_address = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"noreply@m.zitadel.cloud\"";
min_length: 1;
max_length: 200;
}
];
string sender_name = 2 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"ZITADEL\"";
min_length: 1;
max_length: 200;
}
];
bool tls = 3;
string host = 4 [
(validate.rules).string = {min_len: 1, max_len: 500},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"smtp.postmarkapp.com:587\"";
description: "Make sure to include the port.";
min_length: 1;
max_length: 500;
}
];
string user = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"197f0117-529e-443d-bf6c-0292dd9a02b7\"";
}
];
string password = 6 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"this-is-my-password\"";
}
];
string reply_to_address = 7 [
(validate.rules).string = {min_len: 0, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"replyto@m.zitadel.cloud\"";
min_length: 0;
max_length: 200;
}
];
string description = 8 [
(validate.rules).string = {min_len: 0, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"provider description\"";
min_length: 0;
max_length: 200;
}
];
}
message AddEmailProviderSMTPResponse {
zitadel.v1.ObjectDetails details = 1;
string id = 2;
}
message UpdateEmailProviderSMTPRequest {
string sender_address = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"noreply@m.zitadel.cloud\"";
min_length: 1;
max_length: 200;
}
];
string sender_name = 2 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"ZITADEL\"";
min_length: 1;
max_length: 200;
}
];
bool tls = 3;
string host = 4 [
(validate.rules).string = {min_len: 1, max_len: 500},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"smtp.postmarkapp.com:587\"";
description: "Make sure to include the port.";
min_length: 1;
max_length: 500;
}
];
string user = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"197f0117-529e-443d-bf6c-0292dd9a02b7\"";
}
];
string reply_to_address = 6 [
(validate.rules).string = {min_len: 0, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"replyto@m.zitadel.cloud\"";
min_length: 0;
max_length: 200;
}
];
string password = 7 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"this-is-my-password\"";
}
];
string description = 8 [
(validate.rules).string = {min_len: 0, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"provider description\"";
min_length: 0;
max_length: 200;
}
];
string id = 9 [(validate.rules).string = {min_len: 1, max_len: 100}];
}
message UpdateEmailProviderSMTPResponse {
zitadel.v1.ObjectDetails details = 1;
}
message UpdateEmailProviderSMTPPasswordRequest {
string password = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"this-is-my-updated-password\"";
}
];
string id = 2 [(validate.rules).string = {min_len: 1, max_len: 100}];
}
message UpdateEmailProviderSMTPPasswordResponse {
zitadel.v1.ObjectDetails details = 1;
}
message AddEmailProviderHTTPRequest {
string endpoint = 1 [
(validate.rules).string = {min_len: 1, max_len: 2048},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"http://relay.example.com/provider\"";
min_length: 1;
max_length: 2048;
}
];
string description = 2 [
(validate.rules).string = {min_len: 0, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"provider description\"";
min_length: 0;
max_length: 200;
}
];
}
message AddEmailProviderHTTPResponse {
zitadel.v1.ObjectDetails details = 1;
string id = 2;
}
message UpdateEmailProviderHTTPRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 100}];
string endpoint = 2 [
(validate.rules).string = {min_len: 1, max_len: 2048},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"http://relay.example.com/provider\"";
min_length: 1;
max_length: 2048;
}
];
string description = 3 [
(validate.rules).string = {min_len: 0, max_len: 200},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"provider description\"";
min_length: 0;
max_length: 200;
}
];
}
message UpdateEmailProviderHTTPResponse {
zitadel.v1.ObjectDetails details = 1;
}
message ActivateEmailProviderRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
}
message ActivateEmailProviderResponse {
zitadel.v1.ObjectDetails details = 1;
}
message DeactivateEmailProviderRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
}
message DeactivateEmailProviderResponse {
zitadel.v1.ObjectDetails details = 1;
}
message RemoveEmailProviderRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 100}];
}
message RemoveEmailProviderResponse {
zitadel.v1.ObjectDetails details = 1;
}
message TestEmailProviderSMTPByIdRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 100}];
string receiver_address = 2 [
(validate.rules).string = {min_len: 1, max_len: 200, email: true},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"noreply@m.zitadel.cloud\"";
min_length: 1;
max_length: 200;
}
];
}
// This is an empty response
message TestEmailProviderSMTPByIdResponse {}
message TestEmailProviderSMTPRequest {
string sender_address = 1 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"noreply@m.zitadel.cloud\"";
min_length: 1;
max_length: 200;
}
];
string sender_name = 2 [
(validate.rules).string = {min_len: 1, max_len: 200},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"ZITADEL\"";
min_length: 1;
max_length: 200;
}
];
bool tls = 3;
string host = 4 [
(validate.rules).string = {min_len: 1, max_len: 500},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"smtp.postmarkapp.com:587\"";
description: "Make sure to include the port.";
min_length: 1;
max_length: 500;
}
];
string user = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"197f0117-529e-443d-bf6c-0292dd9a02b7\"";
}
];
string password = 6 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"this-is-my-password\"";
}
];
string receiver_address = 7 [
(validate.rules).string = {min_len: 1, max_len: 200, email: true},
(google.api.field_behavior) = REQUIRED,
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"noreply@m.zitadel.cloud\"";
min_length: 1;
max_length: 200;
}
];
string id = 8 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
description: "Zitadel SMTP provider id in case you are not sending the password and want to reuse the stored password";
example: "\"267191369515139464\"";
}
];
}
// This is an empty response
message TestEmailProviderSMTPResponse {}
message ListSMSProvidersRequest { message ListSMSProvidersRequest {
//list limitations and ordering //list limitations and ordering
zitadel.v1.ListQuery query = 1; zitadel.v1.ListQuery query = 1;

View File

@ -95,6 +95,57 @@ message SMTPConfig {
string id = 10; string id = 10;
} }
message EmailProvider {
zitadel.v1.ObjectDetails details = 1;
string id = 2;
EmailProviderState state = 3;
string description = 6;
oneof config {
EmailProviderSMTP smtp = 4;
EmailProviderHTTP http = 5;
}
}
enum EmailProviderState {
EMAIL_PROVIDER_STATE_UNSPECIFIED = 0;
EMAIL_PROVIDER_ACTIVE = 1;
EMAIL_PROVIDER_INACTIVE = 2;
}
message EmailProviderSMTP {
string sender_address = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"noreply@m.zitadel.cloud\"";
}
];
string sender_name = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"ZITADEL\"";
}
];
bool tls = 3;
string host = 4 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"smtp.postmarkapp.com:587\"";
}
];
string user = 5 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"197f0117-529e-443d-bf6c-0292dd9a02b7\"";
}
];
string reply_to_address = 6 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"replyto@m.zitadel.cloud\"";
}
];
}
message EmailProviderHTTP {
string endpoint = 1;
}
message SMSProvider { message SMSProvider {
zitadel.v1.ObjectDetails details = 1; zitadel.v1.ObjectDetails details = 1;
string id = 2; string id = 2;