support init code added

This commit is contained in:
Elio Bischof
2023-09-26 18:37:48 +02:00
parent 22dbcd9f49
commit 0fc36c8c76
2 changed files with 268 additions and 71 deletions

View File

@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
"testing"
"time"
@@ -33,6 +34,7 @@ const (
logoURL = "logo.png"
policyID = "policy1"
userID = "user1"
loginName = "loginName1"
orgID = "org1"
eventOrigin = "https://triggered.here"
assetsPath = "/assets/v1"
@@ -44,41 +46,208 @@ const (
lastEmail = "last@email.com"
)
func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
type fields struct {
queries *mock.MockQueries
commands *mock.MockCommands
es *eventstore.Eventstore
userDataCrypto crypto.EncryptionAlgorithm
SMTPPasswordCrypto crypto.EncryptionAlgorithm
SMSTokenCrypto crypto.EncryptionAlgorithm
}
type args struct {
event eventstore.Event
}
type want struct {
message messages.Email
err assert.ErrorAssertionFunc
}
queries := mock.NewMockQueries(gomock.NewController(t))
func Test_userNotifier_reduceInitCodeAdded(t *testing.T) {
expectMailSubject := "Initialize User"
tests := []struct {
name string
test func() (fields, args, want)
test func(*gomock.Controller, *mock.MockQueries, *mock.MockCommands) (fields, args, want)
}{{
name: "asset url with event trigger url",
test: func() (f fields, a args, w want) {
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
givenTemplate := "{{.LogoURL}}"
expectContent := fmt.Sprintf("%s%s/%s/%s", eventOrigin, assetsPath, policyID, logoURL)
w.message = messages.Email{
Recipients: []string{lastEmail},
Subject: "Verify email",
Subject: expectMailSubject,
Content: expectContent,
}
codeAlg, code := cryptoValue(t, "testcode")
smtpAlg, _ := cryptoValue(t, "smtppw")
codeAlg, code := cryptoValue(t, ctrl, "testcode")
smtpAlg, _ := cryptoValue(t, ctrl, "smtppw")
queries.EXPECT().NotificationProviderByIDAndType(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&query.DebugNotificationProvider{}, nil)
expectTemplateQueries(queries, givenTemplate)
commands.EXPECT().HumanInitCodeSent(gomock.Any(), orgID, userID).Return(nil)
return fields{
queries: queries,
commands: commands,
es: eventstore.NewEventstore(eventstore.TestConfig(
es_repo_mock.NewRepo(t).ExpectFilterEvents(),
)),
userDataCrypto: codeAlg,
SMTPPasswordCrypto: smtpAlg,
SMSTokenCrypto: nil,
}, args{
event: &user.HumanInitialCodeAddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{
AggregateID: userID,
ResourceOwner: sql.NullString{String: orgID},
CreationDate: time.Now().UTC(),
}),
Code: code,
Expiry: time.Hour,
TriggeredAtOrigin: eventOrigin,
},
}, w
},
}, {
name: "asset url without event trigger url",
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
givenTemplate := "{{.LogoURL}}"
expectContent := fmt.Sprintf("%s://%s:%d%s/%s/%s", externalProtocol, instancePrimaryDomain, externalPort, assetsPath, policyID, logoURL)
w.message = messages.Email{
Recipients: []string{lastEmail},
Subject: expectMailSubject,
Content: expectContent,
}
codeAlg, code := cryptoValue(t, ctrl, "testcode")
smtpAlg, _ := cryptoValue(t, ctrl, "smtppw")
queries.EXPECT().NotificationProviderByIDAndType(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&query.DebugNotificationProvider{}, nil)
queries.EXPECT().SearchInstanceDomains(gomock.Any(), gomock.Any()).Return(&query.InstanceDomains{
Domains: []*query.InstanceDomain{{
Domain: instancePrimaryDomain,
IsPrimary: true,
}},
}, nil)
expectTemplateQueries(queries, givenTemplate)
commands.EXPECT().HumanInitCodeSent(gomock.Any(), orgID, userID).Return(nil)
return fields{
queries: queries,
commands: commands,
es: eventstore.NewEventstore(eventstore.TestConfig(
es_repo_mock.NewRepo(t).ExpectFilterEvents(),
)),
userDataCrypto: codeAlg,
SMTPPasswordCrypto: smtpAlg,
}, args{
event: &user.HumanInitialCodeAddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{
AggregateID: userID,
ResourceOwner: sql.NullString{String: orgID},
CreationDate: time.Now().UTC(),
}),
Code: code,
Expiry: time.Hour,
},
}, w
},
}, {
name: "button url with event trigger url",
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
givenTemplate := "{{.URL}}"
testCode := "testcode"
expectContent := fmt.Sprintf("%s/ui/login/user/init?userID=%s&loginname=%s&code=%s&orgID=%s&passwordset=%t", eventOrigin, userID, loginName, testCode, orgID, false)
w.message = messages.Email{
Recipients: []string{lastEmail},
Subject: expectMailSubject,
Content: expectContent,
}
codeAlg, code := cryptoValue(t, ctrl, testCode)
smtpAlg, _ := cryptoValue(t, ctrl, "smtppw")
queries.EXPECT().NotificationProviderByIDAndType(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&query.DebugNotificationProvider{}, nil)
expectTemplateQueries(queries, givenTemplate)
commands.EXPECT().HumanInitCodeSent(gomock.Any(), orgID, userID).Return(nil)
return fields{
queries: queries,
commands: commands,
es: eventstore.NewEventstore(eventstore.TestConfig(
es_repo_mock.NewRepo(t).ExpectFilterEvents(),
)),
userDataCrypto: codeAlg,
SMTPPasswordCrypto: smtpAlg,
SMSTokenCrypto: nil,
}, args{
event: &user.HumanInitialCodeAddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{
AggregateID: userID,
ResourceOwner: sql.NullString{String: orgID},
CreationDate: time.Now().UTC(),
}),
Code: code,
Expiry: time.Hour,
TriggeredAtOrigin: eventOrigin,
},
}, w
},
}, {
name: "button url without event trigger url",
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
givenTemplate := "{{.URL}}"
testCode := "testcode"
expectContent := fmt.Sprintf("%s://%s:%d/ui/login/user/init?userID=%s&loginname=%s&code=%s&orgID=%s&passwordset=%t", externalProtocol, instancePrimaryDomain, externalPort, userID, loginName, testCode, orgID, false)
w.message = messages.Email{
Recipients: []string{lastEmail},
Subject: expectMailSubject,
Content: expectContent,
}
codeAlg, code := cryptoValue(t, ctrl, testCode)
smtpAlg, _ := cryptoValue(t, ctrl, "smtppw")
queries.EXPECT().NotificationProviderByIDAndType(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&query.DebugNotificationProvider{}, nil)
queries.EXPECT().SearchInstanceDomains(gomock.Any(), gomock.Any()).Return(&query.InstanceDomains{
Domains: []*query.InstanceDomain{{
Domain: instancePrimaryDomain,
IsPrimary: true,
}},
}, nil)
expectTemplateQueries(queries, givenTemplate)
commands.EXPECT().HumanInitCodeSent(gomock.Any(), orgID, userID).Return(nil)
return fields{
queries: queries,
commands: commands,
es: eventstore.NewEventstore(eventstore.TestConfig(
es_repo_mock.NewRepo(t).ExpectFilterEvents(),
)),
userDataCrypto: codeAlg,
SMTPPasswordCrypto: smtpAlg,
SMSTokenCrypto: nil,
}, args{
event: &user.HumanInitialCodeAddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{
AggregateID: userID,
ResourceOwner: sql.NullString{String: orgID},
CreationDate: time.Now().UTC(),
}),
Code: code,
Expiry: time.Hour,
},
}, w
},
}}
fs, err := statik_fs.NewWithNamespace("notification")
assert.NoError(t, err)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
queries := mock.NewMockQueries(ctrl)
commands := mock.NewMockCommands(ctrl)
f, a, w := tt.test(ctrl, queries, commands)
_, err = newUserNotifier(ctrl, fs, f, a, w).reduceInitCodeAdded(a.event)
if w.err != nil {
w.err(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
expectMailSubject := "Verify email"
tests := []struct {
name string
test func(*gomock.Controller, *mock.MockQueries, *mock.MockCommands) (fields, args, want)
}{{
name: "asset url with event trigger url",
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
givenTemplate := "{{.LogoURL}}"
expectContent := fmt.Sprintf("%s%s/%s/%s", eventOrigin, assetsPath, policyID, logoURL)
w.message = messages.Email{
Recipients: []string{lastEmail},
Subject: expectMailSubject,
Content: expectContent,
}
codeAlg, code := cryptoValue(t, ctrl, "testcode")
smtpAlg, _ := cryptoValue(t, ctrl, "smtppw")
queries.EXPECT().NotificationProviderByIDAndType(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&query.DebugNotificationProvider{}, nil)
expectTemplateQueries(queries, givenTemplate)
commands := mock.NewMockCommands(gomock.NewController(t))
commands.EXPECT().HumanEmailVerificationCodeSent(gomock.Any(), orgID, userID).Return(nil)
return fields{
queries: queries,
@@ -106,16 +275,16 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
},
}, {
name: "asset url without event trigger url",
test: func() (f fields, a args, w want) {
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
givenTemplate := "{{.LogoURL}}"
expectContent := fmt.Sprintf("%s://%s:%d%s/%s/%s", externalProtocol, instancePrimaryDomain, externalPort, assetsPath, policyID, logoURL)
w.message = messages.Email{
Recipients: []string{lastEmail},
Subject: "Verify email",
Subject: expectMailSubject,
Content: expectContent,
}
codeAlg, code := cryptoValue(t, "testcode")
smtpAlg, _ := cryptoValue(t, "smtppw")
codeAlg, code := cryptoValue(t, ctrl, "testcode")
smtpAlg, _ := cryptoValue(t, ctrl, "smtppw")
queries.EXPECT().NotificationProviderByIDAndType(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&query.DebugNotificationProvider{}, nil)
queries.EXPECT().SearchInstanceDomains(gomock.Any(), gomock.Any()).Return(&query.InstanceDomains{
Domains: []*query.InstanceDomain{{
@@ -124,7 +293,6 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
}},
}, nil)
expectTemplateQueries(queries, givenTemplate)
commands := mock.NewMockCommands(gomock.NewController(t))
commands.EXPECT().HumanEmailVerificationCodeSent(gomock.Any(), orgID, userID).Return(nil)
return fields{
queries: queries,
@@ -150,20 +318,19 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
},
}, {
name: "button url with event trigger url",
test: func() (f fields, a args, w want) {
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
givenTemplate := "{{.URL}}"
testCode := "testcode"
expectContent := fmt.Sprintf("%s/ui/login/mail/verification?userID=%s&code=%s&orgID=%s", eventOrigin, userID, testCode, orgID)
w.message = messages.Email{
Recipients: []string{lastEmail},
Subject: "Verify email",
Subject: expectMailSubject,
Content: expectContent,
}
codeAlg, code := cryptoValue(t, testCode)
smtpAlg, _ := cryptoValue(t, "smtppw")
codeAlg, code := cryptoValue(t, ctrl, testCode)
smtpAlg, _ := cryptoValue(t, ctrl, "smtppw")
queries.EXPECT().NotificationProviderByIDAndType(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&query.DebugNotificationProvider{}, nil)
expectTemplateQueries(queries, givenTemplate)
commands := mock.NewMockCommands(gomock.NewController(t))
commands.EXPECT().HumanEmailVerificationCodeSent(gomock.Any(), orgID, userID).Return(nil)
return fields{
queries: queries,
@@ -191,17 +358,17 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
},
}, {
name: "button url without event trigger url",
test: func() (f fields, a args, w want) {
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
givenTemplate := "{{.URL}}"
testCode := "testcode"
expectContent := fmt.Sprintf("%s://%s:%d/ui/login/mail/verification?userID=%s&code=%s&orgID=%s", externalProtocol, instancePrimaryDomain, externalPort, userID, testCode, orgID)
w.message = messages.Email{
Recipients: []string{lastEmail},
Subject: "Verify email",
Subject: expectMailSubject,
Content: expectContent,
}
codeAlg, code := cryptoValue(t, testCode)
smtpAlg, _ := cryptoValue(t, "smtppw")
codeAlg, code := cryptoValue(t, ctrl, testCode)
smtpAlg, _ := cryptoValue(t, ctrl, "smtppw")
queries.EXPECT().NotificationProviderByIDAndType(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&query.DebugNotificationProvider{}, nil)
queries.EXPECT().SearchInstanceDomains(gomock.Any(), gomock.Any()).Return(&query.InstanceDomains{
Domains: []*query.InstanceDomain{{
@@ -210,7 +377,6 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
}},
}, nil)
expectTemplateQueries(queries, givenTemplate)
commands := mock.NewMockCommands(gomock.NewController(t))
commands.EXPECT().HumanEmailVerificationCodeSent(gomock.Any(), orgID, userID).Return(nil)
return fields{
queries: queries,
@@ -237,21 +403,20 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
},
}, {
name: "button url with url template and event trigger url",
test: func() (f fields, a args, w want) {
test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) {
givenTemplate := "{{.URL}}"
urlTemplate := "https://my.custom.url/org/{{.OrgID}}/user/{{.UserID}}/verify/{{.Code}}"
testCode := "testcode"
expectContent := fmt.Sprintf("https://my.custom.url/org/%s/user/%s/verify/%s", orgID, userID, testCode)
w.message = messages.Email{
Recipients: []string{lastEmail},
Subject: "Verify email",
Subject: expectMailSubject,
Content: expectContent,
}
codeAlg, code := cryptoValue(t, testCode)
smtpAlg, _ := cryptoValue(t, "smtppw")
codeAlg, code := cryptoValue(t, ctrl, testCode)
smtpAlg, _ := cryptoValue(t, ctrl, "smtppw")
queries.EXPECT().NotificationProviderByIDAndType(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&query.DebugNotificationProvider{}, nil)
expectTemplateQueries(queries, givenTemplate)
commands := mock.NewMockCommands(gomock.NewController(t))
commands.EXPECT().HumanEmailVerificationCodeSent(gomock.Any(), orgID, userID).Return(nil)
return fields{
queries: queries,
@@ -278,17 +443,47 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
}, w
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fs, err := statik_fs.NewWithNamespace("notification")
assert.NoError(t, err)
f, a, w := tt.test()
channel := channel_mock.NewMockNotificationChannel(gomock.NewController(t))
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
queries := mock.NewMockQueries(ctrl)
commands := mock.NewMockCommands(ctrl)
f, a, w := tt.test(ctrl, queries, commands)
_, err = newUserNotifier(ctrl, fs, f, a, w).reduceEmailCodeAdded(a.event)
if w.err != nil {
w.err(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
type fields struct {
queries *mock.MockQueries
commands *mock.MockCommands
es *eventstore.Eventstore
userDataCrypto crypto.EncryptionAlgorithm
SMTPPasswordCrypto crypto.EncryptionAlgorithm
SMSTokenCrypto crypto.EncryptionAlgorithm
}
type args struct {
event eventstore.Event
}
type want struct {
message messages.Email
err assert.ErrorAssertionFunc
}
func newUserNotifier(ctrl *gomock.Controller, fs http.FileSystem, f fields, a args, w want) *userNotifier {
channel := channel_mock.NewMockNotificationChannel(ctrl)
if w.err == nil {
w.message.TriggeringEvent = a.event
channel.EXPECT().HandleMessage(&w.message).Return(nil)
}
u := &userNotifier{
return &userNotifier{
commands: f.commands,
queries: NewNotificationQueries(
f.queries,
@@ -304,14 +499,6 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) {
),
channels: &channels{Chain: *senders.ChainChannels(channel)},
}
_, err = u.reduceEmailCodeAdded(a.event)
if w.err != nil {
w.err(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
var _ types.ChannelChains = (*channels)(nil)
@@ -340,13 +527,18 @@ func expectTemplateQueries(queries *mock.MockQueries, template string) {
},
}, nil)
queries.EXPECT().MailTemplateByOrg(gomock.Any(), gomock.Any(), gomock.Any()).Return(&query.MailTemplate{Template: []byte(template)}, nil)
queries.EXPECT().GetNotifyUserByID(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&query.NotifyUser{ID: userID, ResourceOwner: orgID, LastEmail: lastEmail}, nil)
queries.EXPECT().GetNotifyUserByID(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&query.NotifyUser{
ID: userID,
ResourceOwner: orgID,
LastEmail: lastEmail,
PreferredLoginName: loginName,
}, nil)
queries.EXPECT().GetDefaultLanguage(gomock.Any()).Return(language.English)
queries.EXPECT().CustomTextListByTemplate(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2).Return(&query.CustomTexts{}, nil)
}
func cryptoValue(t *testing.T, value string) (*crypto.MockEncryptionAlgorithm, *crypto.CryptoValue) {
encAlg := crypto.NewMockEncryptionAlgorithm(gomock.NewController(t))
func cryptoValue(t *testing.T, ctrl *gomock.Controller, value string) (*crypto.MockEncryptionAlgorithm, *crypto.CryptoValue) {
encAlg := crypto.NewMockEncryptionAlgorithm(ctrl)
encAlg.EXPECT().Algorithm().AnyTimes().Return("enc")
encAlg.EXPECT().EncryptionKeyID().AnyTimes().Return("id")
encAlg.EXPECT().DecryptionKeyIDs().AnyTimes().Return([]string{"id"})

View File

@@ -244,6 +244,7 @@ type HumanInitialCodeAddedEvent struct {
eventstore.BaseEvent `json:"-"`
Code *crypto.CryptoValue `json:"code,omitempty"`
Expiry time.Duration `json:"expiry,omitempty"`
TriggeredAtOrigin string `json:"base_url,omitempty"`
}
func (e *HumanInitialCodeAddedEvent) Data() interface{} {
@@ -254,6 +255,10 @@ func (e *HumanInitialCodeAddedEvent) UniqueConstraints() []*eventstore.EventUniq
return nil
}
func (e *HumanInitialCodeAddedEvent) TriggerOrigin() string {
return e.TriggeredAtOrigin
}
func NewHumanInitialCodeAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,