mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:17:32 +00:00
feat: Add Twilio Verification Service (#8678)
# Which Problems Are Solved Twilio supports a robust, multi-channel verification service that notably supports multi-region SMS sender numbers required for our use case. Currently, Zitadel does much of the work of the Twilio Verify (eg. localization, code generation, messaging) but doesn't support the pool of sender numbers that Twilio Verify does. # How the Problems Are Solved To support this API, we need to be able to store the Twilio Service ID and send that in a verification request where appropriate: phone number verification and SMS 2FA code paths. This PR does the following: - Adds the ability to use Twilio Verify of standard messaging through Twilio - Adds support for international numbers and more reliable verification messages sent from multiple numbers - Adds a new Twilio configuration option to support Twilio Verify in the admin console - Sends verification SMS messages through Twilio Verify - Implements Twilio Verification Checks for codes generated through the same # Additional Changes # Additional Context - base was implemented by @zhirschtritt in https://github.com/zitadel/zitadel/pull/8268 ❤️ - closes https://github.com/zitadel/zitadel/issues/8581 --------- Co-authored-by: Zachary Hirschtritt <zachary.hirschtritt@klaviyo.com> Co-authored-by: Joey Biscoglia <joey.biscoglia@klaviyo.com>
This commit is contained in:
@@ -13,14 +13,29 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/notification/senders"
|
||||
"github.com/zitadel/zitadel/internal/notification/senders/mock"
|
||||
"github.com/zitadel/zitadel/internal/repository/instance"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||
defaultGenerators := &SecretGenerators{
|
||||
OTPSMS: &crypto.GeneratorConfig{
|
||||
Length: 8,
|
||||
Expiry: time.Hour,
|
||||
IncludeLowerLetters: true,
|
||||
IncludeUpperLetters: true,
|
||||
IncludeDigits: true,
|
||||
IncludeSymbols: true,
|
||||
},
|
||||
}
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
userEncryption crypto.EncryptionAlgorithm
|
||||
defaultSecretGenerators *SecretGenerators
|
||||
newEncryptedCodeWithDefault encryptedCodeWithDefaultFunc
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -41,9 +56,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "invalid phone, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -61,8 +74,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "user not existing, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
@@ -83,8 +95,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "phone not changed, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -126,8 +137,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "verified phone changed, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -186,8 +196,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "phone changed to verified, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -242,8 +251,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "phone changed to verified, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -298,8 +306,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "phone changed with code, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -316,6 +323,36 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSMSConfigActivatedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("instanceID").Aggregate,
|
||||
"id",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSMSConfigTwilioAddedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("instanceID").Aggregate,
|
||||
"id",
|
||||
"",
|
||||
"sid",
|
||||
"senderNumber",
|
||||
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("crypted")},
|
||||
"",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
instance.NewSMSConfigActivatedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("instanceID").Aggregate,
|
||||
"id",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
@@ -330,9 +367,100 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
userEncryption: crypto.NewMockEncryptionAlgorithm(gomock.NewController(t)),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("a", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
email: &domain.Phone{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "user1",
|
||||
},
|
||||
PhoneNumber: "+41711234567",
|
||||
},
|
||||
resourceOwner: "org1",
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
},
|
||||
res: res{
|
||||
want: &domain.Phone{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "user1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
PhoneNumber: "+41711234567",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "phone changed with code (external), ok",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSMSConfigActivatedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("instanceID").Aggregate,
|
||||
"id",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSMSConfigTwilioAddedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("instanceID").Aggregate,
|
||||
"id",
|
||||
"",
|
||||
"sid",
|
||||
"senderNumber",
|
||||
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("crypted")},
|
||||
"verifyServiceSID",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
instance.NewSMSConfigActivatedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("instanceID").Aggregate,
|
||||
"id",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"+41711234567",
|
||||
),
|
||||
user.NewHumanPhoneCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
nil,
|
||||
0,
|
||||
"id",
|
||||
),
|
||||
),
|
||||
),
|
||||
userEncryption: crypto.NewMockEncryptionAlgorithm(gomock.NewController(t)),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -359,7 +487,10 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
userEncryption: tt.fields.userEncryption,
|
||||
defaultSecretGenerators: tt.fields.defaultSecretGenerators,
|
||||
newEncryptedCodeWithDefault: tt.fields.newEncryptedCodeWithDefault,
|
||||
}
|
||||
got, err := r.ChangeHumanPhone(tt.args.ctx, tt.args.email, tt.args.resourceOwner, tt.args.secretGenerator)
|
||||
if tt.res.err == nil {
|
||||
@@ -377,7 +508,8 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
|
||||
|
||||
func TestCommandSide_VerifyHumanPhone(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
phoneCodeVerifier func(ctx context.Context, id string) (senders.CodeGenerator, error)
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -399,9 +531,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "userid missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -415,9 +545,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "code missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -431,8 +559,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "user not existing, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
@@ -449,8 +576,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "code not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -482,8 +608,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "invalid code, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -515,6 +640,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -539,8 +665,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "valid code, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -572,6 +697,7 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -595,11 +721,80 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid code (external), ok",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"+411234567",
|
||||
),
|
||||
),
|
||||
eventFromEventPusherWithCreationDateNow(
|
||||
user.NewHumanPhoneCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
nil,
|
||||
0,
|
||||
"id",
|
||||
),
|
||||
),
|
||||
eventFromEventPusherWithCreationDateNow(
|
||||
user.NewHumanPhoneCodeSentEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&senders.CodeGeneratorInfo{
|
||||
ID: "id",
|
||||
VerificationID: "verificationID",
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanPhoneVerifiedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
),
|
||||
),
|
||||
),
|
||||
phoneCodeVerifier: func(ctx context.Context, id string) (senders.CodeGenerator, error) {
|
||||
sender := mock.NewMockCodeGenerator(gomock.NewController(t))
|
||||
sender.EXPECT().VerifyCode("verificationID", "a")
|
||||
return sender, nil
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
code: "a",
|
||||
resourceOwner: "org1",
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
phoneCodeVerifier: tt.fields.phoneCodeVerifier,
|
||||
}
|
||||
got, err := r.VerifyHumanPhone(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.resourceOwner, tt.args.secretGenerator)
|
||||
if tt.res.err == nil {
|
||||
@@ -616,9 +811,21 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
|
||||
defaultGenerators := &SecretGenerators{
|
||||
OTPSMS: &crypto.GeneratorConfig{
|
||||
Length: 8,
|
||||
Expiry: time.Hour,
|
||||
IncludeLowerLetters: true,
|
||||
IncludeUpperLetters: true,
|
||||
IncludeDigits: true,
|
||||
IncludeSymbols: true,
|
||||
},
|
||||
}
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
userEncryption crypto.EncryptionAlgorithm
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
userEncryption crypto.EncryptionAlgorithm
|
||||
defaultSecretGenerators *SecretGenerators
|
||||
newEncryptedCodeWithDefault encryptedCodeWithDefaultFunc
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -638,9 +845,7 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "userid missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -653,8 +858,7 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "user not existing, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
@@ -670,8 +874,7 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "phone already verified, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -713,8 +916,7 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "new code, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -739,16 +941,33 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSecretGeneratorAddedEvent(context.Background(),
|
||||
instance.NewSMSConfigActivatedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("instanceID").Aggregate,
|
||||
domain.SecretGeneratorTypeVerifyPhoneCode,
|
||||
8,
|
||||
time.Hour,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
)),
|
||||
"id",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSMSConfigTwilioAddedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("instanceID").Aggregate,
|
||||
"id",
|
||||
"",
|
||||
"sid",
|
||||
"senderNumber",
|
||||
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("crypted")},
|
||||
"",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
instance.NewSMSConfigActivatedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("instanceID").Aggregate,
|
||||
"id",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanPhoneCodeAddedEvent(context.Background(),
|
||||
@@ -760,10 +979,92 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
|
||||
Crypted: []byte("12345678"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
userEncryption: crypto.CreateMockEncryptionAlgWithCode(gomock.NewController(t), "12345678"),
|
||||
userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
newEncryptedCodeWithDefault: mockEncryptedCodeWithDefault("12345678", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new code (external), ok",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"+411234567",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSMSConfigActivatedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("instanceID").Aggregate,
|
||||
"id",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSMSConfigTwilioAddedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("instanceID").Aggregate,
|
||||
"id",
|
||||
"",
|
||||
"sid",
|
||||
"senderNumber",
|
||||
&crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "id", Crypted: []byte("crypted")},
|
||||
"verifyServiceSID",
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
instance.NewSMSConfigActivatedEvent(
|
||||
context.Background(),
|
||||
&instance.NewAggregate("instanceID").Aggregate,
|
||||
"id",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanPhoneCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
nil,
|
||||
0,
|
||||
"id",
|
||||
),
|
||||
),
|
||||
),
|
||||
userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
defaultSecretGenerators: defaultGenerators,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -780,8 +1081,10 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
userEncryption: tt.fields.userEncryption,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
userEncryption: tt.fields.userEncryption,
|
||||
defaultSecretGenerators: tt.fields.defaultSecretGenerators,
|
||||
newEncryptedCodeWithDefault: tt.fields.newEncryptedCodeWithDefault,
|
||||
}
|
||||
got, err := r.CreateHumanPhoneVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
|
||||
if tt.res.err == nil {
|
||||
@@ -799,12 +1102,13 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
|
||||
|
||||
func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
userID string
|
||||
resourceOwner string
|
||||
generatorInfo *senders.CodeGeneratorInfo
|
||||
}
|
||||
type res struct {
|
||||
err func(error) bool
|
||||
@@ -818,9 +1122,7 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
|
||||
{
|
||||
name: "userid missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -833,8 +1135,7 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
|
||||
{
|
||||
name: "user not existing, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
@@ -850,8 +1151,7 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
|
||||
{
|
||||
name: "code sent, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -877,6 +1177,7 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
|
||||
expectPush(
|
||||
user.NewHumanPhoneCodeSentEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&senders.CodeGeneratorInfo{},
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -885,6 +1186,55 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
generatorInfo: &senders.CodeGeneratorInfo{},
|
||||
},
|
||||
res: res{},
|
||||
},
|
||||
{
|
||||
name: "code sent (external), ok",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanPhoneChangedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"+411234567",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanPhoneCodeSentEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&senders.CodeGeneratorInfo{
|
||||
ID: "generatorID",
|
||||
VerificationID: "verificationID",
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
resourceOwner: "org1",
|
||||
generatorInfo: &senders.CodeGeneratorInfo{
|
||||
ID: "generatorID",
|
||||
VerificationID: "verificationID",
|
||||
},
|
||||
},
|
||||
res: res{},
|
||||
},
|
||||
@@ -892,9 +1242,9 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
}
|
||||
err := r.HumanPhoneVerificationCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID)
|
||||
err := r.HumanPhoneVerificationCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.generatorInfo)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -907,7 +1257,7 @@ func TestCommandSide_PhoneVerificationCodeSent(t *testing.T) {
|
||||
|
||||
func TestCommandSide_RemoveHumanPhone(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
eventstore func(*testing.T) *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -927,9 +1277,7 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "userid missing, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
eventstore: expectEventstore(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
@@ -942,8 +1290,7 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "user not existing, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
@@ -959,8 +1306,7 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "phone not existing, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -991,8 +1337,7 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) {
|
||||
{
|
||||
name: "remove phone, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
@@ -1037,7 +1382,7 @@ func TestCommandSide_RemoveHumanPhone(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
}
|
||||
got, err := r.RemoveHumanPhone(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
|
||||
if tt.res.err == nil {
|
||||
|
Reference in New Issue
Block a user