mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-06 22:32:16 +00:00
feat: added xoauth for smtp (#11239)
# Which Problems Are Solved - Some mail providers (e.g.: MS Exchange) have deprecated plain auth # How the Problems Are Solved Adds XOauth2 auth option to SMPT - SMTP config now can contain XOAuth2 auth config - Proto files are updated to have a oneof for configuration selection. Old fields for configuring plain auth are still available not to create a breaking change. These can be removed in the future. - Columns are added to the database to persist the config # Additional Changes none # Additional Context Replace this example with links to related issues, discussions, discord threads, or other sources with more context. Use the Closing #issue syntax for issues that are resolved with this PR. - Closes #8042 --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Tim Möhlmann <[email protected]>
This commit is contained in:
co-authored by
Copilot
Tim Möhlmann
parent
96bbd6a65c
commit
3fc0ef2506
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
@@ -29,10 +30,11 @@ type SMTPConfig struct {
|
||||
TLS bool
|
||||
Host string
|
||||
User string
|
||||
Password *crypto.CryptoValue
|
||||
SenderAddress string
|
||||
SenderName string
|
||||
ReplyToAddress string
|
||||
PlainAuth *instance.PlainAuth
|
||||
XOAuth2Auth *instance.XOAuth2Auth
|
||||
}
|
||||
|
||||
func NewIAMSMTPConfigWriteModel(instanceID, id, domain string) *IAMSMTPConfigWriteModel {
|
||||
@@ -85,7 +87,11 @@ func (wm *IAMSMTPConfigWriteModel) Reduce() error {
|
||||
continue
|
||||
}
|
||||
if e.Password != nil {
|
||||
wm.SMTPConfig.Password = e.Password
|
||||
if wm.SMTPConfig.PlainAuth == nil {
|
||||
wm.SMTPConfig.PlainAuth = &instance.PlainAuth{Password: e.Password}
|
||||
} else {
|
||||
wm.SMTPConfig.PlainAuth.Password = e.Password
|
||||
}
|
||||
}
|
||||
case *instance.SMTPConfigHTTPAddedEvent:
|
||||
if wm.ID != e.ID {
|
||||
@@ -157,7 +163,19 @@ func (wm *IAMSMTPConfigWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
Builder()
|
||||
}
|
||||
|
||||
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 string,
|
||||
smtpUser string,
|
||||
plainAuth *instance.PlainAuth,
|
||||
xoauth2Auth *instance.XOAuth2Auth,
|
||||
) (*instance.SMTPConfigChangedEvent, bool, error) {
|
||||
changes := make([]instance.SMTPConfigChanges, 0)
|
||||
var err error
|
||||
if wm.SMTPConfig == nil {
|
||||
@@ -188,9 +206,13 @@ func (wm *IAMSMTPConfigWriteModel) NewChangedEvent(ctx context.Context, aggregat
|
||||
if wm.SMTPConfig.User != smtpUser {
|
||||
changes = append(changes, instance.ChangeSMTPConfigSMTPUser(smtpUser))
|
||||
}
|
||||
if smtpPassword != nil {
|
||||
changes = append(changes, instance.ChangeSMTPConfigSMTPPassword(smtpPassword))
|
||||
if plainAuth != nil {
|
||||
changes = append(changes, smtpPlainAuthChanges(wm.SMTPConfig.PlainAuth, *plainAuth)...)
|
||||
}
|
||||
if xoauth2Auth != nil {
|
||||
changes = append(changes, smtpXOAuthChanges(wm.SMTPConfig.XOAuth2Auth, *xoauth2Auth)...)
|
||||
}
|
||||
|
||||
if len(changes) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
@@ -201,6 +223,77 @@ func (wm *IAMSMTPConfigWriteModel) NewChangedEvent(ctx context.Context, aggregat
|
||||
return changeEvent, true, nil
|
||||
}
|
||||
|
||||
func smtpPlainAuthChanges(wm *instance.PlainAuth, auth instance.PlainAuth) []instance.SMTPConfigChanges {
|
||||
// if no auth is yet present, set both
|
||||
if wm == nil {
|
||||
return []instance.SMTPConfigChanges{
|
||||
instance.ChangeSMTPConfigSMTPPassword(auth.Password),
|
||||
}
|
||||
}
|
||||
|
||||
// if auth is already present, add changes for the changed values
|
||||
var changes []instance.SMTPConfigChanges
|
||||
|
||||
if auth.Password != nil {
|
||||
changes = append(changes, instance.ChangeSMTPConfigSMTPPassword(auth.Password))
|
||||
}
|
||||
|
||||
return changes
|
||||
}
|
||||
|
||||
func smtpXOAuthChanges(wm *instance.XOAuth2Auth, auth instance.XOAuth2Auth) []instance.SMTPConfigChanges {
|
||||
// if no auth is yet present, set all properties
|
||||
if wm == nil {
|
||||
return []instance.SMTPConfigChanges{
|
||||
instance.ChangeSMTPConfigXOAuth2ClientCredentialsClientId(auth.ClientCredentials.ClientId),
|
||||
instance.ChangeSMTPConfigXOAuth2ClientCredentialsClientSecret(auth.ClientCredentials.ClientSecret),
|
||||
instance.ChangeSMTPConfigXOAuth2TokenEndpoint(auth.TokenEndpoint),
|
||||
instance.ChangeSMTPConfigXOAuth2Scopes(auth.Scopes),
|
||||
}
|
||||
}
|
||||
|
||||
// if auth is already present, add changes for the changed values
|
||||
var changes []instance.SMTPConfigChanges
|
||||
|
||||
if wm.TokenEndpoint != auth.TokenEndpoint {
|
||||
changes = append(changes, instance.ChangeSMTPConfigXOAuth2TokenEndpoint(auth.TokenEndpoint))
|
||||
}
|
||||
if len(wm.Scopes) != len(auth.Scopes) {
|
||||
changes = append(changes, instance.ChangeSMTPConfigXOAuth2Scopes(auth.Scopes))
|
||||
} else {
|
||||
for _, s := range auth.Scopes {
|
||||
if !slices.Contains(wm.Scopes, s) {
|
||||
changes = append(changes, instance.ChangeSMTPConfigXOAuth2Scopes(auth.Scopes))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if auth.ClientCredentials != nil {
|
||||
changes = append(changes, smtpXOAuthClientCredentialChanges(auth.ClientCredentials, *auth.ClientCredentials)...)
|
||||
}
|
||||
|
||||
return changes
|
||||
}
|
||||
|
||||
func smtpXOAuthClientCredentialChanges(wm *instance.XOAuth2ClientCredentials, cc instance.XOAuth2ClientCredentials) []instance.SMTPConfigChanges {
|
||||
if wm == nil {
|
||||
return []instance.SMTPConfigChanges{
|
||||
instance.ChangeSMTPConfigXOAuth2ClientCredentialsClientId(cc.ClientId),
|
||||
instance.ChangeSMTPConfigXOAuth2ClientCredentialsClientSecret(cc.ClientSecret),
|
||||
}
|
||||
}
|
||||
|
||||
var changes []instance.SMTPConfigChanges
|
||||
if wm.ClientId != cc.ClientId {
|
||||
changes = append(changes, instance.ChangeSMTPConfigXOAuth2ClientCredentialsClientId(cc.ClientId))
|
||||
}
|
||||
if wm.ClientSecret != cc.ClientSecret {
|
||||
changes = append(changes, instance.ChangeSMTPConfigXOAuth2ClientCredentialsClientSecret(cc.ClientSecret))
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
func (wm *IAMSMTPConfigWriteModel) NewHTTPChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
@@ -242,11 +335,30 @@ func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigAddedEvent(e *instance.SMTPCo
|
||||
TLS: e.TLS,
|
||||
Host: e.Host,
|
||||
User: e.User,
|
||||
Password: e.Password,
|
||||
SenderName: e.SenderName,
|
||||
SenderAddress: e.SenderAddress,
|
||||
ReplyToAddress: e.ReplyToAddress,
|
||||
}
|
||||
|
||||
if e.PlainAuth != nil {
|
||||
wm.SMTPConfig.PlainAuth = &instance.PlainAuth{
|
||||
Password: e.PlainAuth.Password,
|
||||
}
|
||||
}
|
||||
if e.XOAuth2Auth != nil {
|
||||
wm.SMTPConfig.XOAuth2Auth = &instance.XOAuth2Auth{
|
||||
TokenEndpoint: e.XOAuth2Auth.TokenEndpoint,
|
||||
Scopes: e.XOAuth2Auth.Scopes,
|
||||
}
|
||||
|
||||
if e.XOAuth2Auth.ClientCredentials != nil {
|
||||
wm.SMTPConfig.XOAuth2Auth.ClientCredentials = &instance.XOAuth2ClientCredentials{
|
||||
ClientId: e.XOAuth2Auth.ClientCredentials.ClientId,
|
||||
ClientSecret: e.XOAuth2Auth.ClientCredentials.ClientSecret,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -290,9 +402,6 @@ func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigChangedEvent(e *instance.SMTP
|
||||
if e.User != nil {
|
||||
wm.SMTPConfig.User = *e.User
|
||||
}
|
||||
if e.Password != nil {
|
||||
wm.SMTPConfig.Password = e.Password
|
||||
}
|
||||
if e.FromAddress != nil {
|
||||
wm.SMTPConfig.SenderAddress = *e.FromAddress
|
||||
}
|
||||
@@ -303,6 +412,39 @@ func (wm *IAMSMTPConfigWriteModel) reduceSMTPConfigChangedEvent(e *instance.SMTP
|
||||
wm.SMTPConfig.ReplyToAddress = *e.ReplyToAddress
|
||||
}
|
||||
|
||||
if !e.PlainAuth.IsEmpty() {
|
||||
if wm.SMTPConfig.PlainAuth == nil {
|
||||
wm.SMTPConfig.PlainAuth = &instance.PlainAuth{}
|
||||
wm.SMTPConfig.XOAuth2Auth = nil
|
||||
}
|
||||
if e.PlainAuth.Password != nil {
|
||||
wm.SMTPConfig.PlainAuth.Password = e.PlainAuth.Password
|
||||
} else if e.Password != nil {
|
||||
wm.SMTPConfig.PlainAuth.Password = e.Password
|
||||
}
|
||||
}
|
||||
|
||||
if !e.XOAuth2Auth.IsEmpty() {
|
||||
if wm.SMTPConfig.XOAuth2Auth == nil {
|
||||
wm.SMTPConfig.XOAuth2Auth = &instance.XOAuth2Auth{}
|
||||
wm.SMTPConfig.PlainAuth = nil
|
||||
}
|
||||
if e.XOAuth2Auth.TokenEndpoint != nil {
|
||||
wm.SMTPConfig.XOAuth2Auth.TokenEndpoint = *e.XOAuth2Auth.TokenEndpoint
|
||||
}
|
||||
if e.XOAuth2Auth.Scopes != nil {
|
||||
wm.SMTPConfig.XOAuth2Auth.Scopes = e.XOAuth2Auth.Scopes
|
||||
}
|
||||
if wm.SMTPConfig.XOAuth2Auth.ClientCredentials != nil && !e.XOAuth2Auth.ClientCredentials.IsEmpty() {
|
||||
if e.XOAuth2Auth.ClientCredentials.ClientId != nil {
|
||||
wm.SMTPConfig.XOAuth2Auth.ClientCredentials.ClientId = *e.XOAuth2Auth.ClientCredentials.ClientId
|
||||
}
|
||||
if e.XOAuth2Auth.ClientCredentials.ClientSecret != nil {
|
||||
wm.SMTPConfig.XOAuth2Auth.ClientCredentials.ClientSecret = e.XOAuth2Auth.ClientCredentials.ClientSecret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 == "" {
|
||||
|
||||
Reference in New Issue
Block a user