fix: user init mail (for wrong email) (#891)

* add resendInitialMail

* disable email notifications (when not initialised)

* fix resend init mail

* add tests

* cleanup

* cleanup

* fix tests

* add resend trigger, dialog

* refactor contact component, add sendinitmail fnc

* skip email if empty

* reload user on phone email changes, i18n warndialog on dl

* lint

* rebuild mgmt proto

* remove initial focus

* Update console/src/assets/i18n/de.json

Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>

Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
This commit is contained in:
Livio Amstutz
2020-11-16 11:43:22 +01:00
committed by GitHub
parent 69c39b5eb2
commit 376fba72d8
42 changed files with 11465 additions and 18601 deletions

View File

@@ -737,6 +737,9 @@ func (es *UserEventstore) RequestSetPassword(ctx context.Context, userID string,
if user.Human == nil {
return errors.ThrowPreconditionFailed(nil, "EVENT-33ywz", "Errors.User.NotHuman")
}
if user.State == usr_model.UserStateInitial {
return errors.ThrowPreconditionFailed(nil, "EVENT-Hs11s", "Errors.User.NotInitialised")
}
passwordCode := new(model.PasswordCode)
err = es.generatePasswordCode(passwordCode, notifyType)
@@ -754,6 +757,35 @@ func (es *UserEventstore) RequestSetPassword(ctx context.Context, userID string,
return nil
}
func (es *UserEventstore) ResendInitialMail(ctx context.Context, userID, email string) error {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-G4bmn", "Errors.User.UserIDMissing")
}
user, err := es.UserByID(ctx, userID)
if err != nil {
return err
}
if user.Human == nil {
return errors.ThrowPreconditionFailed(nil, "EVENT-Hfsww", "Errors.User.NotHuman")
}
if user.State != usr_model.UserStateInitial {
return errors.ThrowPreconditionFailed(nil, "EVENT-BGbbe", "Errors.User.AlreadyInitialised")
}
err = user.GenerateInitCodeIfNeeded(es.InitializeUserCode)
if err != nil {
return err
}
repoUser := model.UserFromModel(user)
agg := ResendInitialPasswordAggregate(es.AggregateCreator(), repoUser, user.InitCode, email)
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, agg)
if err != nil {
return err
}
es.userCache.cacheUser(repoUser)
return nil
}
func (es *UserEventstore) PasswordCodeSent(ctx context.Context, userID string) error {
if userID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-s09ow", "Errors.User.UserIDMissing")
@@ -946,6 +978,9 @@ func (es *UserEventstore) ChangeEmail(ctx context.Context, email *usr_model.Emai
if user.Human == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-tgBdL", "Errors.User.NotHuman")
}
if user.State == usr_model.UserStateInitial {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-3H4q", "Errors.User.NotInitialised")
}
emailCode, err := email.GenerateEmailCodeIfNeeded(es.EmailVerificationCode)
if err != nil {
@@ -1018,6 +1053,9 @@ func (es *UserEventstore) CreateEmailVerificationCode(ctx context.Context, userI
if user.Human == nil {
return errors.ThrowPreconditionFailed(nil, "EVENT-hqUZP", "Errors.User.NotHuman")
}
if user.State == usr_model.UserStateInitial {
return errors.ThrowPreconditionFailed(nil, "EVENT-E3fbw", "Errors.User.NotInitialised")
}
if user.Email == nil {
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-pdo9s", "Errors.User.EmailNotFound")
}

View File

@@ -4,15 +4,15 @@ import (
"encoding/json"
"time"
"github.com/caos/zitadel/internal/id"
"github.com/golang/mock/gomock"
mock_cache "github.com/caos/zitadel/internal/cache/mock"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore/mock"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/id"
global_model "github.com/caos/zitadel/internal/model"
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
"github.com/golang/mock/gomock"
)
func GetMockedEventstore(ctrl *gomock.Controller, mockEs *mock.MockEventstore) *UserEventstore {
@@ -162,11 +162,18 @@ func GetMockManipulateUserWithPasswordAndEmailCodeGen(ctrl *gomock.Controller, u
return GetMockedEventstoreWithPw(ctrl, mockEs, false, true, false, true)
}
func GetMockManipulateUserWithEmailCodeGen(ctrl *gomock.Controller, user model.User) *UserEventstore {
func GetMockManipulateUserWithEmailCodeGen(ctrl *gomock.Controller, user model.User, verified bool) *UserEventstore {
data, _ := json.Marshal(user)
events := []*es_models.Event{
{AggregateID: "AggregateID", AggregateVersion: "v1", Sequence: 1, Type: model.UserAdded, Data: data},
}
if verified {
email, _ := json.Marshal(model.Email{EmailAddress: "address"})
events = append(events,
&es_models.Event{AggregateID: "AggregateID", AggregateVersion: "v1", Sequence: 2, Type: model.HumanEmailVerified},
&es_models.Event{AggregateID: "AggregateID", AggregateVersion: "v1", Sequence: 3, Type: model.HumanEmailChanged, Data: email},
)
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
@@ -186,12 +193,15 @@ func GetMockManipulateUserWithPhoneCodeGen(ctrl *gomock.Controller, user model.U
return GetMockedEventstoreWithPw(ctrl, mockEs, false, false, true, false)
}
func GetMockManipulateUserWithPasswordCodeGen(ctrl *gomock.Controller, user model.User) *UserEventstore {
func GetMockManipulateUserWithPasswordCodeGen(ctrl *gomock.Controller, user model.User, verified bool) *UserEventstore {
data, _ := json.Marshal(user)
code, _ := json.Marshal(user.PasswordCode)
events := []*es_models.Event{
{AggregateID: "AggregateID", AggregateVersion: "v1", Sequence: 1, Type: model.UserAdded, Data: data},
{AggregateID: "AggregateID", AggregateVersion: "v1", Sequence: 1, Type: model.UserPasswordCodeAdded, Data: code},
{AggregateID: "AggregateID", AggregateVersion: "v1", Sequence: 2, Type: model.UserPasswordCodeAdded, Data: code},
}
if verified {
events = append(events, &es_models.Event{AggregateID: "AggregateID", AggregateVersion: "v1", Sequence: 3, Type: model.HumanEmailVerified})
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
@@ -382,7 +392,7 @@ func GetMockManipulateUserVerifiedPhone(ctrl *gomock.Controller) *UserEventstore
return GetMockedEventstore(ctrl, mockEs)
}
func GetMockManipulateUserFull(ctrl *gomock.Controller) *UserEventstore {
func GetMockManipulateUserFull(ctrl *gomock.Controller, verified bool) *UserEventstore {
user := model.Human{
Profile: &model.Profile{
DisplayName: "DisplayName",
@@ -407,6 +417,9 @@ func GetMockManipulateUserFull(ctrl *gomock.Controller) *UserEventstore {
events := []*es_models.Event{
{AggregateID: "AggregateID", AggregateVersion: "v1", Sequence: 1, Type: model.UserAdded, Data: dataUser},
}
if verified {
events = append(events, &es_models.Event{AggregateID: "AggregateID", AggregateVersion: "v1", Sequence: 2, Type: model.HumanEmailVerified})
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))

View File

@@ -3,7 +3,6 @@ package eventsourcing
import (
"context"
"encoding/json"
iam_model "github.com/caos/zitadel/internal/iam/model"
"net"
"testing"
"time"
@@ -15,6 +14,7 @@ import (
"github.com/caos/zitadel/internal/crypto"
caos_errs "github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/user/model"
repo_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
)
@@ -1287,7 +1287,7 @@ func TestPasswordID(t *testing.T) {
{
name: "get by id, ok",
args: args{
es: GetMockManipulateUserFull(ctrl),
es: GetMockManipulateUserFull(ctrl, false),
ctx: authz.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
@@ -1366,7 +1366,7 @@ func TestSetOneTimePassword(t *testing.T) {
{
name: "create one time pw",
args: args{
es: GetMockManipulateUserWithPasswordCodeGen(ctrl, repo_model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, Human: &repo_model.Human{}}),
es: GetMockManipulateUserWithPasswordCodeGen(ctrl, repo_model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, Human: &repo_model.Human{Email: &repo_model.Email{EmailAddress: "email"}}}, true),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.PasswordComplexityPolicyView{},
password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SecretString: "Password"},
@@ -1516,8 +1516,10 @@ func TestCheckPassword(t *testing.T) {
Algorithm: "hash",
Crypted: []byte("password"),
}},
Email: &repo_model.Email{EmailAddress: "email"},
},
},
true,
),
ctx: authz.NewMockContext("orgID", "userID"),
userID: "userID",
@@ -1582,8 +1584,10 @@ func TestSetPassword(t *testing.T) {
KeyID: "id",
Crypted: []byte("code"),
}},
Email: &repo_model.Email{EmailAddress: "email"},
},
},
true,
),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.PasswordComplexityPolicyView{},
@@ -1627,8 +1631,11 @@ func TestSetPassword(t *testing.T) {
es: GetMockManipulateUserWithPasswordCodeGen(ctrl,
repo_model.User{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
Human: &repo_model.Human{},
Human: &repo_model.Human{
Email: &repo_model.Email{EmailAddress: "email"},
},
},
true,
),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.PasswordComplexityPolicyView{},
@@ -1653,8 +1660,10 @@ func TestSetPassword(t *testing.T) {
KeyID: "id",
Crypted: []byte("code2"),
}},
Email: &repo_model.Email{EmailAddress: "email"},
},
},
true,
),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.PasswordComplexityPolicyView{},
@@ -1759,8 +1768,11 @@ func TestChangePassword(t *testing.T) {
es: GetMockManipulateUserWithPasswordCodeGen(ctrl,
repo_model.User{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
Human: &repo_model.Human{},
Human: &repo_model.Human{
Email: &repo_model.Email{EmailAddress: "email"},
},
},
true,
),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.PasswordComplexityPolicyView{},
@@ -1784,8 +1796,10 @@ func TestChangePassword(t *testing.T) {
Algorithm: "hash",
Crypted: []byte("older"),
}},
Email: &repo_model.Email{EmailAddress: "email"},
},
},
true,
),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.PasswordComplexityPolicyView{},
@@ -1859,7 +1873,12 @@ func TestRequestSetPassword(t *testing.T) {
{
name: "create pw",
args: args{
es: GetMockManipulateUserWithPasswordCodeGen(ctrl, repo_model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, Human: &repo_model.Human{}}),
es: GetMockManipulateUserWithPasswordCodeGen(ctrl, repo_model.User{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"},
Human: &repo_model.Human{
Email: &repo_model.Email{EmailAddress: "email"},
}},
true),
ctx: authz.NewMockContext("orgID", "userID"),
userID: "AggregateID",
notifyType: model.NotificationTypeEmail,
@@ -1868,6 +1887,18 @@ func TestRequestSetPassword(t *testing.T) {
password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, ChangeRequired: false},
},
},
{
name: "initial state",
args: args{
es: GetMockManipulateUserWithPasswordCodeGen(ctrl, repo_model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, Human: &repo_model.Human{}}, false),
ctx: authz.NewMockContext("orgID", "userID"),
userID: "AggregateID",
notifyType: model.NotificationTypeEmail,
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "empty userid",
args: args{
@@ -1905,6 +1936,84 @@ func TestRequestSetPassword(t *testing.T) {
}
}
func TestResendInitialMail(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *UserEventstore
ctx context.Context
userID string
mail string
}
type res struct {
password *model.Password
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "resend ok",
args: args{
es: GetMockManipulateUserWithInitCode(ctrl, repo_model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, Human: &repo_model.Human{}}),
ctx: authz.NewMockContext("orgID", "userID"),
userID: "AggregateID",
mail: "",
},
res: res{
password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, ChangeRequired: false},
},
},
{
name: "resend with email ok",
args: args{
es: GetMockManipulateUserWithInitCode(ctrl, repo_model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, Human: &repo_model.Human{}}),
ctx: authz.NewMockContext("orgID", "userID"),
userID: "AggregateID",
mail: "email",
},
res: res{
password: &model.Password{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, ChangeRequired: false},
},
},
{
name: "empty userid",
args: args{
es: GetMockManipulateUser(ctrl),
mail: "",
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing user not found",
args: args{
es: GetMockManipulateUserNoEvents(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
userID: "AggregateID",
mail: "",
},
res: res{
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.args.es.ResendInitialMail(tt.args.ctx, tt.args.userID, tt.args.mail)
if tt.res.errFunc == nil && err != nil {
t.Errorf("should not get err")
}
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestPasswordCodeSent(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
@@ -2170,7 +2279,7 @@ func TestProfileByID(t *testing.T) {
{
name: "get by id, ok",
args: args{
es: GetMockManipulateUserFull(ctrl),
es: GetMockManipulateUserFull(ctrl, false),
ctx: authz.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Human: &model.Human{}},
},
@@ -2237,7 +2346,7 @@ func TestChangeProfile(t *testing.T) {
{
name: "get by id, ok",
args: args{
es: GetMockManipulateUserFull(ctrl),
es: GetMockManipulateUserFull(ctrl, false),
ctx: authz.NewMockContext("orgID", "userID"),
profile: &model.Profile{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, FirstName: "FirstName Changed", LastName: "LastName Changed"},
},
@@ -2304,7 +2413,7 @@ func TestEmailByID(t *testing.T) {
{
name: "get by id, ok",
args: args{
es: GetMockManipulateUserFull(ctrl),
es: GetMockManipulateUserFull(ctrl, false),
ctx: authz.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
@@ -2371,7 +2480,7 @@ func TestChangeEmail(t *testing.T) {
{
name: "change email address, verified",
args: args{
es: GetMockManipulateUserFull(ctrl),
es: GetMockManipulateUserFull(ctrl, true),
ctx: authz.NewMockContext("orgID", "userID"),
email: &model.Email{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, EmailAddress: "EmailAddressChanged", IsEmailVerified: true},
},
@@ -2389,7 +2498,7 @@ func TestChangeEmail(t *testing.T) {
Profile: &repo_model.Profile{DisplayName: "DisplayName"},
Email: &repo_model.Email{EmailAddress: "EmailAddress"},
},
}),
}, true),
ctx: authz.NewMockContext("orgID", "userID"),
email: &model.Email{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, EmailAddress: "EmailAddressChanged", IsEmailVerified: false},
},
@@ -2397,6 +2506,24 @@ func TestChangeEmail(t *testing.T) {
email: &model.Email{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, EmailAddress: "EmailAddressChanged", IsEmailVerified: false},
},
},
{
name: "user state initial",
args: args{
es: GetMockManipulateUserWithEmailCodeGen(ctrl, repo_model.User{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1},
UserName: "UserName",
Human: &repo_model.Human{
Profile: &repo_model.Profile{DisplayName: "DisplayName"},
Email: &repo_model.Email{EmailAddress: "EmailAddress"},
},
}, false),
ctx: authz.NewMockContext("orgID", "userID"),
email: &model.Email{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, EmailAddress: "EmailAddressChanged", IsEmailVerified: false},
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "empty userid",
args: args{
@@ -2423,7 +2550,9 @@ func TestChangeEmail(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.ChangeEmail(tt.args.ctx, tt.args.email)
if (tt.res.errFunc != nil && !tt.res.errFunc(err)) || (tt.res.errFunc == nil && err != nil) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.errFunc == nil && result.AggregateID == "" {
t.Errorf("result has no id")
}
@@ -2433,9 +2562,6 @@ func TestChangeEmail(t *testing.T) {
if tt.res.errFunc == nil && result.IsEmailVerified != tt.res.email.IsEmailVerified {
t.Errorf("got wrong result change required: expected: %v, actual: %v ", tt.res.email.IsEmailVerified, result.IsEmailVerified)
}
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
@@ -2551,12 +2677,30 @@ func TestCreateEmailVerificationCode(t *testing.T) {
Human: &repo_model.Human{
Profile: &repo_model.Profile{DisplayName: "DisplayName"},
Email: &repo_model.Email{EmailAddress: "EmailAddress"},
}}),
}}, true),
ctx: authz.NewMockContext("orgID", "userID"),
userID: "userID",
},
res: res{},
},
{
name: "initial state",
args: args{
es: GetMockManipulateUserWithEmailCodeGen(ctrl, repo_model.User{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1},
UserName: "UserName",
Human: &repo_model.Human{
Profile: &repo_model.Profile{DisplayName: "DisplayName"},
Email: &repo_model.Email{EmailAddress: "EmailAddress"},
},
}, false),
ctx: authz.NewMockContext("orgID", "userID"),
userID: "userID",
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "empty userid",
args: args{
@@ -2606,7 +2750,7 @@ func TestCreateEmailVerificationCode(t *testing.T) {
err := tt.args.es.CreateEmailVerificationCode(tt.args.ctx, tt.args.userID)
if tt.res.errFunc == nil && err != nil {
t.Errorf("should not ger err")
t.Errorf("should not get err, got: %v", err)
}
if tt.res.errFunc != nil && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
@@ -2695,7 +2839,7 @@ func TestPhoneByID(t *testing.T) {
{
name: "get by id, ok",
args: args{
es: GetMockManipulateUserFull(ctrl),
es: GetMockManipulateUserFull(ctrl, false),
ctx: authz.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
@@ -2762,7 +2906,7 @@ func TestChangePhone(t *testing.T) {
{
name: "change phone, verified",
args: args{
es: GetMockManipulateUserFull(ctrl),
es: GetMockManipulateUserFull(ctrl, false),
ctx: authz.NewMockContext("orgID", "userID"),
phone: &model.Phone{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, PhoneNumber: "0711234567", IsPhoneVerified: true},
},
@@ -3149,7 +3293,7 @@ func TestAddressByID(t *testing.T) {
{
name: "get by id, ok",
args: args{
es: GetMockManipulateUserFull(ctrl),
es: GetMockManipulateUserFull(ctrl, false),
ctx: authz.NewMockContext("orgID", "userID"),
user: &model.User{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
@@ -3216,7 +3360,7 @@ func TestChangeAddress(t *testing.T) {
{
name: "change address ok",
args: args{
es: GetMockManipulateUserFull(ctrl),
es: GetMockManipulateUserFull(ctrl, false),
ctx: authz.NewMockContext("orgID", "userID"),
address: &model.Address{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Country: "CountryChanged"},
},

View File

@@ -189,7 +189,7 @@ func (h *Human) AppendEvent(event *es_models.Event) (err error) {
}
func (h *Human) ComputeObject() {
if h.State == int32(model.UserStateUnspecified) {
if h.State == int32(model.UserStateUnspecified) || h.State == int32(model.UserStateInitial) {
if h.Email != nil && h.IsEmailVerified {
h.State = int32(model.UserStateActive)
} else {

View File

@@ -2,14 +2,15 @@ package eventsourcing
import (
"context"
"github.com/caos/zitadel/internal/api/authz"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
"strings"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
)
@@ -454,6 +455,25 @@ func RequestSetPassword(aggCreator *es_models.AggregateCreator, user *model.User
}
}
func ResendInitialPasswordAggregate(aggCreator *es_models.AggregateCreator, user *model.User, code *usr_model.InitUserCode, email string) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if code == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dfs3q", "Errors.Internal")
}
agg, err := UserAggregate(ctx, aggCreator, user)
if err != nil {
return nil, err
}
if email != "" && user.Email != nil && email != user.Email.EmailAddress {
agg, err = agg.AppendEvent(model.HumanEmailChanged, map[string]interface{}{"email": email})
if err != nil {
return nil, err
}
}
return agg.AppendEvent(model.InitializedHumanCodeAdded, code)
}
}
func PasswordCodeSentAggregate(aggCreator *es_models.AggregateCreator, user *model.User) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
agg, err := UserAggregate(ctx, aggCreator, user)

View File

@@ -5,11 +5,11 @@ import (
"testing"
"time"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/crypto"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
)
@@ -1199,6 +1199,114 @@ func TestRequestSetPasswordAggregate(t *testing.T) {
}
}
func TestResendInitialPasswordAggregate(t *testing.T) {
type args struct {
ctx context.Context
user *model.User
aggCreator *models.AggregateCreator
initcode *usr_model.InitUserCode
email string
}
type res struct {
eventLen int
eventType models.EventType
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "resend initial password aggregate ok",
args: args{
ctx: authz.NewMockContext("orgID", "userID"),
user: &model.User{
ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
UserName: "UserName",
Human: &model.Human{
Profile: &model.Profile{DisplayName: "DisplayName"},
Email: &model.Email{},
},
},
aggCreator: models.NewAggregateCreator("Test"),
initcode: &usr_model.InitUserCode{Expiry: time.Hour * 1},
},
res: res{
eventLen: 1,
},
},
{
name: "resend initial password with same email ok",
args: args{
ctx: authz.NewMockContext("orgID", "userID"),
user: &model.User{
ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
UserName: "UserName",
Human: &model.Human{
Profile: &model.Profile{DisplayName: "DisplayName"},
Email: &model.Email{EmailAddress: "email"},
},
},
aggCreator: models.NewAggregateCreator("Test"),
initcode: &usr_model.InitUserCode{Expiry: time.Hour * 1},
email: "email",
},
res: res{
eventLen: 1,
},
},
{
name: "resend initial password with new email ok",
args: args{
ctx: authz.NewMockContext("orgID", "userID"),
user: &model.User{
ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
UserName: "UserName",
Human: &model.Human{
Profile: &model.Profile{DisplayName: "DisplayName"},
Email: &model.Email{EmailAddress: "old"},
},
},
aggCreator: models.NewAggregateCreator("Test"),
initcode: &usr_model.InitUserCode{Expiry: time.Hour * 1},
email: "new",
},
res: res{
eventLen: 2,
},
},
{
name: "request nil",
args: args{
ctx: authz.NewMockContext("orgID", "userID"),
user: &model.User{
ObjectRoot: models.ObjectRoot{AggregateID: "ID"},
UserName: "UserName",
Human: &model.Human{
Profile: &model.Profile{DisplayName: "DisplayName"},
},
},
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg, err := ResendInitialPasswordAggregate(tt.args.aggCreator, tt.args.user, tt.args.initcode, tt.args.email)(tt.args.ctx)
if (tt.res.errFunc == nil && err != nil) || (tt.res.errFunc != nil && !tt.res.errFunc(err)) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.errFunc == nil && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
}
})
}
}
func TestPasswordCodeSentAggregate(t *testing.T) {
type args struct {
ctx context.Context