mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 01:37:31 +00:00
feat: user v3 contact email and phone (#8644)
# Which Problems Are Solved Endpoints to maintain email and phone contact on user v3 are not implemented. # How the Problems Are Solved Add 3 endpoints with SetContactEmail, VerifyContactEmail and ResendContactEmailCode. Add 3 endpoints with SetContactPhone, VerifyContactPhone and ResendContactPhoneCode. Refactor the logic how contact is managed in the user creation and update. # Additional Changes None # Additional Context - part of https://github.com/zitadel/zitadel/issues/6433 --------- Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
@@ -189,6 +189,9 @@ type AppendReducer interface {
|
||||
}
|
||||
|
||||
func (c *Commands) pushAppendAndReduce(ctx context.Context, object AppendReducer, cmds ...eventstore.Command) error {
|
||||
if len(cmds) == 0 {
|
||||
return nil
|
||||
}
|
||||
events, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -196,6 +199,20 @@ func (c *Commands) pushAppendAndReduce(ctx context.Context, object AppendReducer
|
||||
return AppendAndReduce(object, events...)
|
||||
}
|
||||
|
||||
type AppendReducerDetails interface {
|
||||
AppendEvents(...eventstore.Event)
|
||||
// TODO: Why is it allowed to return an error here?
|
||||
Reduce() error
|
||||
GetWriteModel() *eventstore.WriteModel
|
||||
}
|
||||
|
||||
func (c *Commands) pushAppendAndReduceDetails(ctx context.Context, object AppendReducerDetails, cmds ...eventstore.Command) (*domain.ObjectDetails, error) {
|
||||
if err := c.pushAppendAndReduce(ctx, object, cmds...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(object.GetWriteModel()), nil
|
||||
}
|
||||
|
||||
func AppendAndReduce(object AppendReducer, events ...eventstore.Event) error {
|
||||
object.AppendEvents(events...)
|
||||
return object.Reduce()
|
||||
|
@@ -6,17 +6,13 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
domain_schema "github.com/zitadel/zitadel/internal/domain/schema"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/user/schemauser"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type CreateSchemaUser struct {
|
||||
Details *domain.ObjectDetails
|
||||
|
||||
SchemaID string
|
||||
schemaRevision uint64
|
||||
|
||||
@@ -25,9 +21,9 @@ type CreateSchemaUser struct {
|
||||
Data json.RawMessage
|
||||
|
||||
Email *Email
|
||||
ReturnCodeEmail string
|
||||
ReturnCodeEmail *string
|
||||
Phone *Phone
|
||||
ReturnCodePhone string
|
||||
ReturnCodePhone *string
|
||||
}
|
||||
|
||||
func (s *CreateSchemaUser) Valid(ctx context.Context, c *Commands) (err error) {
|
||||
@@ -99,44 +95,36 @@ func (c *Commands) getSchemaRoleForWrite(ctx context.Context, resourceOwner, use
|
||||
return domain_schema.RoleOwner, nil
|
||||
}
|
||||
|
||||
func (c *Commands) CreateSchemaUser(ctx context.Context, user *CreateSchemaUser, alg crypto.EncryptionAlgorithm) (err error) {
|
||||
func (c *Commands) CreateSchemaUser(ctx context.Context, user *CreateSchemaUser) (*domain.ObjectDetails, error) {
|
||||
if err := user.Valid(ctx, c); err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
writeModel, err := c.getSchemaUserExists(ctx, user.ResourceOwner, user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if writeModel.Exists() {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-Nn8CRVlkeZ", "Errors.User.AlreadyExists")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userAgg := UserV3AggregateFromWriteModel(&writeModel.WriteModel)
|
||||
events := []eventstore.Command{
|
||||
schemauser.NewCreatedEvent(ctx,
|
||||
userAgg,
|
||||
user.SchemaID, user.schemaRevision, user.Data,
|
||||
),
|
||||
events, codeEmail, codePhone, err := writeModel.NewCreated(ctx,
|
||||
user.SchemaID,
|
||||
user.schemaRevision,
|
||||
user.Data,
|
||||
user.Email,
|
||||
user.Phone,
|
||||
func(ctx context.Context) (*EncryptedCode, error) {
|
||||
return c.newEmailCode(ctx, c.eventstore.Filter, c.userEncryption) //nolint:staticcheck
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user.Email != nil {
|
||||
events, user.ReturnCodeEmail, err = c.updateSchemaUserEmail(ctx, writeModel, events, userAgg, user.Email, alg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if codeEmail != "" {
|
||||
user.ReturnCodeEmail = &codeEmail
|
||||
}
|
||||
if user.Phone != nil {
|
||||
events, user.ReturnCodePhone, err = c.updateSchemaUserPhone(ctx, writeModel, events, userAgg, user.Phone, alg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if codePhone != "" {
|
||||
user.ReturnCodePhone = &codePhone
|
||||
}
|
||||
|
||||
if err := c.pushAppendAndReduce(ctx, writeModel, events...); err != nil {
|
||||
return err
|
||||
}
|
||||
user.Details = writeModelToObjectDetails(&writeModel.WriteModel)
|
||||
return nil
|
||||
return c.pushAppendAndReduceDetails(ctx, writeModel, events...)
|
||||
}
|
||||
|
||||
func (c *Commands) DeleteSchemaUser(ctx context.Context, resourceOwner, id string) (*domain.ObjectDetails, error) {
|
||||
@@ -147,50 +135,38 @@ func (c *Commands) DeleteSchemaUser(ctx context.Context, resourceOwner, id strin
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !writeModel.Exists() {
|
||||
return nil, zerrors.ThrowNotFound(nil, "COMMAND-syHyCsGmvM", "Errors.User.NotFound")
|
||||
}
|
||||
if err := c.checkPermissionDeleteUser(ctx, writeModel.ResourceOwner, writeModel.AggregateID); err != nil {
|
||||
|
||||
events, err := writeModel.NewDelete(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.pushAppendAndReduce(ctx, writeModel,
|
||||
schemauser.NewDeletedEvent(ctx, UserV3AggregateFromWriteModel(&writeModel.WriteModel)),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
|
||||
return c.pushAppendAndReduceDetails(ctx, writeModel, events...)
|
||||
}
|
||||
|
||||
type ChangeSchemaUser struct {
|
||||
Details *domain.ObjectDetails
|
||||
|
||||
SchemaID *string
|
||||
schemaWriteModel *UserSchemaWriteModel
|
||||
|
||||
ResourceOwner string
|
||||
ID string
|
||||
Data json.RawMessage
|
||||
|
||||
SchemaUser *SchemaUser
|
||||
|
||||
Email *Email
|
||||
ReturnCodeEmail string
|
||||
ReturnCodeEmail *string
|
||||
Phone *Phone
|
||||
ReturnCodePhone string
|
||||
ReturnCodePhone *string
|
||||
}
|
||||
|
||||
func (s *ChangeSchemaUser) Valid(ctx context.Context, c *Commands) (err error) {
|
||||
type SchemaUser struct {
|
||||
SchemaID string
|
||||
Data json.RawMessage
|
||||
}
|
||||
|
||||
func (s *ChangeSchemaUser) Valid() (err error) {
|
||||
if s.ID == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-gEJR1QOGHb", "Errors.IDMissing")
|
||||
}
|
||||
if s.SchemaID != nil {
|
||||
s.schemaWriteModel, err = c.getSchemaWriteModelByID(ctx, "", *s.SchemaID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !s.schemaWriteModel.Exists() {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-VLDTtxT3If", "Errors.UserSchema.NotExists")
|
||||
}
|
||||
}
|
||||
|
||||
if s.Email != nil && s.Email.Address != "" {
|
||||
if err := s.Email.Validate(); err != nil {
|
||||
return err
|
||||
@@ -206,92 +182,56 @@ func (s *ChangeSchemaUser) Valid(ctx context.Context, c *Commands) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ChangeSchemaUser) ValidData(ctx context.Context, c *Commands, existingUser *UserV3WriteModel) (err error) {
|
||||
// get role for permission check in schema through extension
|
||||
role, err := c.getSchemaRoleForWrite(ctx, existingUser.ResourceOwner, existingUser.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.schemaWriteModel == nil {
|
||||
s.schemaWriteModel, err = c.getSchemaWriteModelByID(ctx, "", existingUser.SchemaID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
schema, err := domain_schema.NewSchema(role, bytes.NewReader(s.schemaWriteModel.Schema))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if data not changed but a new schema or revision should be used
|
||||
data := s.Data
|
||||
if s.Data == nil {
|
||||
data = existingUser.Data
|
||||
}
|
||||
|
||||
var v interface{}
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-7o3ZGxtXUz", "Errors.User.Invalid")
|
||||
}
|
||||
|
||||
if err := schema.Validate(v); err != nil {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-SlKXqLSeL6", "Errors.UserSchema.Data.Invalid")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeSchemaUser(ctx context.Context, user *ChangeSchemaUser, alg crypto.EncryptionAlgorithm) (err error) {
|
||||
if err := user.Valid(ctx, c); err != nil {
|
||||
return err
|
||||
func (c *Commands) ChangeSchemaUser(ctx context.Context, user *ChangeSchemaUser) (*domain.ObjectDetails, error) {
|
||||
if err := user.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
writeModel, err := c.getSchemaUserWriteModelByID(ctx, user.ResourceOwner, user.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
if !writeModel.Exists() {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "COMMAND-Nn8CRVlkeZ", "Errors.User.NotFound")
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Nn8CRVlkeZ", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
userAgg := UserV3AggregateFromWriteModel(&writeModel.WriteModel)
|
||||
events := make([]eventstore.Command, 0)
|
||||
if user.Data != nil || user.SchemaID != nil {
|
||||
if err := user.ValidData(ctx, c, writeModel); err != nil {
|
||||
return err
|
||||
}
|
||||
updateEvent := writeModel.NewUpdatedEvent(ctx,
|
||||
userAgg,
|
||||
user.schemaWriteModel.AggregateID,
|
||||
user.schemaWriteModel.SchemaRevision,
|
||||
user.Data,
|
||||
)
|
||||
if updateEvent != nil {
|
||||
events = append(events, updateEvent)
|
||||
}
|
||||
schemaID := writeModel.SchemaID
|
||||
if user.SchemaUser != nil && user.SchemaUser.SchemaID != "" {
|
||||
schemaID = user.SchemaUser.SchemaID
|
||||
}
|
||||
if user.Email != nil {
|
||||
events, user.ReturnCodeEmail, err = c.updateSchemaUserEmail(ctx, writeModel, events, userAgg, user.Email, alg)
|
||||
|
||||
var schemaWM *UserSchemaWriteModel
|
||||
if user.SchemaUser != nil {
|
||||
schemaWriteModel, err := c.getSchemaWriteModelByID(ctx, "", schemaID)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if user.Phone != nil {
|
||||
events, user.ReturnCodePhone, err = c.updateSchemaUserPhone(ctx, writeModel, events, userAgg, user.Phone, alg)
|
||||
if err != nil {
|
||||
return err
|
||||
if !schemaWriteModel.Exists() {
|
||||
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-VLDTtxT3If", "Errors.UserSchema.NotExists")
|
||||
}
|
||||
schemaWM = schemaWriteModel
|
||||
}
|
||||
if len(events) == 0 {
|
||||
user.Details = writeModelToObjectDetails(&writeModel.WriteModel)
|
||||
return nil
|
||||
|
||||
events, codeEmail, codePhone, err := writeModel.NewUpdate(ctx,
|
||||
schemaWM,
|
||||
user.SchemaUser,
|
||||
user.Email,
|
||||
user.Phone,
|
||||
func(ctx context.Context) (*EncryptedCode, error) {
|
||||
return c.newEmailCode(ctx, c.eventstore.Filter, c.userEncryption) //nolint:staticcheck
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.pushAppendAndReduce(ctx, writeModel, events...); err != nil {
|
||||
return err
|
||||
|
||||
if codeEmail != "" {
|
||||
user.ReturnCodeEmail = &codeEmail
|
||||
}
|
||||
user.Details = writeModelToObjectDetails(&writeModel.WriteModel)
|
||||
return nil
|
||||
if codePhone != "" {
|
||||
user.ReturnCodePhone = &codePhone
|
||||
}
|
||||
return c.pushAppendAndReduceDetails(ctx, writeModel, events...)
|
||||
}
|
||||
|
||||
func (c *Commands) checkPermissionUpdateUserState(ctx context.Context, resourceOwner, userID string) error {
|
||||
@@ -368,7 +308,7 @@ func (c *Commands) ActivateSchemaUser(ctx context.Context, resourceOwner, id str
|
||||
if id == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-17XupGvxBJ", "Errors.IDMissing")
|
||||
}
|
||||
writeModel, err := c.getSchemaUserExists(ctx, "", id)
|
||||
writeModel, err := c.getSchemaUserExists(ctx, resourceOwner, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -386,65 +326,8 @@ func (c *Commands) ActivateSchemaUser(ctx context.Context, resourceOwner, id str
|
||||
return writeModelToObjectDetails(&writeModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) updateSchemaUserEmail(ctx context.Context, existing *UserV3WriteModel, events []eventstore.Command, agg *eventstore.Aggregate, email *Email, alg crypto.EncryptionAlgorithm) (_ []eventstore.Command, plainCode string, err error) {
|
||||
if existing.Email == string(email.Address) {
|
||||
return events, plainCode, nil
|
||||
}
|
||||
|
||||
events = append(events, schemauser.NewEmailUpdatedEvent(ctx,
|
||||
agg,
|
||||
email.Address,
|
||||
))
|
||||
if email.Verified {
|
||||
events = append(events, schemauser.NewEmailVerifiedEvent(ctx, agg))
|
||||
} else {
|
||||
cryptoCode, err := c.newEmailCode(ctx, c.eventstore.Filter, alg) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if email.ReturnCode {
|
||||
plainCode = cryptoCode.Plain
|
||||
}
|
||||
events = append(events, schemauser.NewEmailCodeAddedEvent(ctx, agg,
|
||||
cryptoCode.Crypted,
|
||||
cryptoCode.Expiry,
|
||||
email.URLTemplate,
|
||||
email.ReturnCode,
|
||||
))
|
||||
}
|
||||
return events, plainCode, nil
|
||||
}
|
||||
|
||||
func (c *Commands) updateSchemaUserPhone(ctx context.Context, existing *UserV3WriteModel, events []eventstore.Command, agg *eventstore.Aggregate, phone *Phone, alg crypto.EncryptionAlgorithm) (_ []eventstore.Command, plainCode string, err error) {
|
||||
if existing.Phone == string(phone.Number) {
|
||||
return events, plainCode, nil
|
||||
}
|
||||
|
||||
events = append(events, schemauser.NewPhoneUpdatedEvent(ctx,
|
||||
agg,
|
||||
phone.Number,
|
||||
))
|
||||
if phone.Verified {
|
||||
events = append(events, schemauser.NewPhoneVerifiedEvent(ctx, agg))
|
||||
} else {
|
||||
cryptoCode, err := c.newPhoneCode(ctx, c.eventstore.Filter, alg) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if phone.ReturnCode {
|
||||
plainCode = cryptoCode.Plain
|
||||
}
|
||||
events = append(events, schemauser.NewPhoneCodeAddedEvent(ctx, agg,
|
||||
cryptoCode.Crypted,
|
||||
cryptoCode.Expiry,
|
||||
phone.ReturnCode,
|
||||
))
|
||||
}
|
||||
return events, plainCode, nil
|
||||
}
|
||||
|
||||
func (c *Commands) getSchemaUserExists(ctx context.Context, resourceOwner, id string) (*UserV3WriteModel, error) {
|
||||
writeModel := NewExistsUserV3WriteModel(resourceOwner, id)
|
||||
writeModel := NewExistsUserV3WriteModel(resourceOwner, id, c.checkPermission)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -452,7 +335,23 @@ func (c *Commands) getSchemaUserExists(ctx context.Context, resourceOwner, id st
|
||||
}
|
||||
|
||||
func (c *Commands) getSchemaUserWriteModelByID(ctx context.Context, resourceOwner, id string) (*UserV3WriteModel, error) {
|
||||
writeModel := NewUserV3WriteModel(resourceOwner, id)
|
||||
writeModel := NewUserV3WriteModel(resourceOwner, id, c.checkPermission)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
||||
|
||||
func (c *Commands) getSchemaUserEmailWriteModelByID(ctx context.Context, resourceOwner, id string) (*UserV3WriteModel, error) {
|
||||
writeModel := NewUserV3EmailWriteModel(resourceOwner, id, c.checkPermission)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
||||
|
||||
func (c *Commands) getSchemaUserPhoneWriteModelByID(ctx context.Context, resourceOwner, id string) (*UserV3WriteModel, error) {
|
||||
writeModel := NewUserV3PhoneWriteModel(resourceOwner, id, c.checkPermission)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
115
internal/command/user_v3_email.go
Normal file
115
internal/command/user_v3_email.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type ChangeSchemaUserEmail struct {
|
||||
ResourceOwner string
|
||||
ID string
|
||||
|
||||
Email *Email
|
||||
ReturnCode *string
|
||||
}
|
||||
|
||||
func (s *ChangeSchemaUserEmail) Valid() (err error) {
|
||||
if s.ID == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-0oj2PquNGA", "Errors.IDMissing")
|
||||
}
|
||||
if s.Email != nil && s.Email.Address != "" {
|
||||
if err := s.Email.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if s.Email != nil && s.Email.URLTemplate != "" {
|
||||
if err := domain.RenderConfirmURLTemplate(io.Discard, s.Email.URLTemplate, s.ID, "code", "orgID"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeSchemaUserEmail(ctx context.Context, user *ChangeSchemaUserEmail) (_ *domain.ObjectDetails, err error) {
|
||||
if err := user.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
writeModel, err := c.getSchemaUserEmailWriteModelByID(ctx, user.ResourceOwner, user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events, plainCode, err := writeModel.NewEmailUpdate(ctx,
|
||||
user.Email,
|
||||
func(ctx context.Context) (*EncryptedCode, error) {
|
||||
return c.newEmailCode(ctx, c.eventstore.Filter, c.userEncryption) //nolint:staticcheck
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if plainCode != "" {
|
||||
user.ReturnCode = &plainCode
|
||||
}
|
||||
return c.pushAppendAndReduceDetails(ctx, writeModel, events...)
|
||||
}
|
||||
|
||||
func (c *Commands) VerifySchemaUserEmail(ctx context.Context, resourceOwner, id, code string) (*domain.ObjectDetails, error) {
|
||||
if id == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-y3n4Sdu8j5", "Errors.IDMissing")
|
||||
}
|
||||
writeModel, err := c.getSchemaUserEmailWriteModelByID(ctx, resourceOwner, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events, err := writeModel.NewEmailVerify(ctx,
|
||||
func(creationDate time.Time, expiry time.Duration, cryptoCode *crypto.CryptoValue) error {
|
||||
return crypto.VerifyCode(creationDate, expiry, cryptoCode, code, c.userEncryption)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.pushAppendAndReduceDetails(ctx, writeModel, events...)
|
||||
}
|
||||
|
||||
type ResendSchemaUserEmailCode struct {
|
||||
ResourceOwner string
|
||||
ID string
|
||||
|
||||
URLTemplate string
|
||||
ReturnCode bool
|
||||
PlainCode *string
|
||||
}
|
||||
|
||||
func (c *Commands) ResendSchemaUserEmailCode(ctx context.Context, user *ResendSchemaUserEmailCode) (*domain.ObjectDetails, error) {
|
||||
if user.ID == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-KvPc5o9GeJ", "Errors.IDMissing")
|
||||
}
|
||||
writeModel, err := c.getSchemaUserEmailWriteModelByID(ctx, user.ResourceOwner, user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events, plainCode, err := writeModel.NewResendEmailCode(ctx,
|
||||
func(ctx context.Context) (*EncryptedCode, error) {
|
||||
return c.newEmailCode(ctx, c.eventstore.Filter, c.userEncryption) //nolint:staticcheck
|
||||
},
|
||||
user.URLTemplate,
|
||||
user.ReturnCode,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if plainCode != "" {
|
||||
user.PlainCode = &plainCode
|
||||
}
|
||||
return c.pushAppendAndReduceDetails(ctx, writeModel, events...)
|
||||
}
|
1076
internal/command/user_v3_email_test.go
Normal file
1076
internal/command/user_v3_email_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,10 +4,15 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
domain_schema "github.com/zitadel/zitadel/internal/domain/schema"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/user/schemauser"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type UserV3WriteModel struct {
|
||||
@@ -23,37 +28,77 @@ type UserV3WriteModel struct {
|
||||
Email string
|
||||
IsEmailVerified bool
|
||||
EmailVerifiedFailedCount int
|
||||
EmailCode *VerifyCode
|
||||
|
||||
Phone string
|
||||
IsPhoneVerified bool
|
||||
PhoneVerifiedFailedCount int
|
||||
PhoneCode *VerifyCode
|
||||
|
||||
Data json.RawMessage
|
||||
|
||||
Locked bool
|
||||
State domain.UserState
|
||||
|
||||
checkPermission domain.PermissionCheck
|
||||
writePermissionCheck bool
|
||||
}
|
||||
|
||||
func NewExistsUserV3WriteModel(resourceOwner, userID string) *UserV3WriteModel {
|
||||
func (wm *UserV3WriteModel) GetWriteModel() *eventstore.WriteModel {
|
||||
return &wm.WriteModel
|
||||
}
|
||||
|
||||
type VerifyCode struct {
|
||||
Code *crypto.CryptoValue
|
||||
CreationDate time.Time
|
||||
Expiry time.Duration
|
||||
}
|
||||
|
||||
func NewExistsUserV3WriteModel(resourceOwner, userID string, checkPermission domain.PermissionCheck) *UserV3WriteModel {
|
||||
return &UserV3WriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
PhoneWM: false,
|
||||
EmailWM: false,
|
||||
DataWM: false,
|
||||
PhoneWM: false,
|
||||
EmailWM: false,
|
||||
DataWM: false,
|
||||
checkPermission: checkPermission,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUserV3WriteModel(resourceOwner, userID string) *UserV3WriteModel {
|
||||
func NewUserV3WriteModel(resourceOwner, userID string, checkPermission domain.PermissionCheck) *UserV3WriteModel {
|
||||
return &UserV3WriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
PhoneWM: true,
|
||||
EmailWM: true,
|
||||
DataWM: true,
|
||||
PhoneWM: true,
|
||||
EmailWM: true,
|
||||
DataWM: true,
|
||||
checkPermission: checkPermission,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUserV3EmailWriteModel(resourceOwner, userID string, checkPermission domain.PermissionCheck) *UserV3WriteModel {
|
||||
return &UserV3WriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
EmailWM: true,
|
||||
checkPermission: checkPermission,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUserV3PhoneWriteModel(resourceOwner, userID string, checkPermission domain.PermissionCheck) *UserV3WriteModel {
|
||||
return &UserV3WriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
PhoneWM: true,
|
||||
checkPermission: checkPermission,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,24 +128,38 @@ func (wm *UserV3WriteModel) Reduce() error {
|
||||
wm.Email = string(e.EmailAddress)
|
||||
wm.IsEmailVerified = false
|
||||
wm.EmailVerifiedFailedCount = 0
|
||||
wm.EmailCode = nil
|
||||
case *schemauser.EmailCodeAddedEvent:
|
||||
wm.IsEmailVerified = false
|
||||
wm.EmailVerifiedFailedCount = 0
|
||||
wm.EmailCode = &VerifyCode{
|
||||
Code: e.Code,
|
||||
CreationDate: e.CreationDate(),
|
||||
Expiry: e.Expiry,
|
||||
}
|
||||
case *schemauser.EmailVerifiedEvent:
|
||||
wm.IsEmailVerified = true
|
||||
wm.EmailVerifiedFailedCount = 0
|
||||
wm.EmailCode = nil
|
||||
case *schemauser.EmailVerificationFailedEvent:
|
||||
wm.EmailVerifiedFailedCount += 1
|
||||
case *schemauser.PhoneUpdatedEvent:
|
||||
wm.Phone = string(e.PhoneNumber)
|
||||
wm.IsPhoneVerified = false
|
||||
wm.PhoneVerifiedFailedCount = 0
|
||||
wm.EmailCode = nil
|
||||
case *schemauser.PhoneCodeAddedEvent:
|
||||
wm.IsPhoneVerified = false
|
||||
wm.PhoneVerifiedFailedCount = 0
|
||||
wm.PhoneCode = &VerifyCode{
|
||||
Code: e.Code,
|
||||
CreationDate: e.CreationDate(),
|
||||
Expiry: e.Expiry,
|
||||
}
|
||||
case *schemauser.PhoneVerifiedEvent:
|
||||
wm.PhoneVerifiedFailedCount = 0
|
||||
wm.IsPhoneVerified = true
|
||||
wm.PhoneCode = nil
|
||||
case *schemauser.PhoneVerificationFailedEvent:
|
||||
wm.PhoneVerifiedFailedCount += 1
|
||||
case *schemauser.LockedEvent:
|
||||
@@ -156,13 +215,159 @@ func (wm *UserV3WriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
EventTypes(eventtypes...).Builder()
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) NewUpdatedEvent(
|
||||
func (wm *UserV3WriteModel) NewCreated(
|
||||
ctx context.Context,
|
||||
agg *eventstore.Aggregate,
|
||||
schemaID string,
|
||||
schemaRevision uint64,
|
||||
data json.RawMessage,
|
||||
) *schemauser.UpdatedEvent {
|
||||
email *Email,
|
||||
phone *Phone,
|
||||
code func(context.Context) (*EncryptedCode, error),
|
||||
) (_ []eventstore.Command, codeEmail string, codePhone string, err error) {
|
||||
if err := wm.checkPermissionWrite(ctx, wm.ResourceOwner, wm.AggregateID); err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
if wm.Exists() {
|
||||
return nil, "", "", zerrors.ThrowPreconditionFailed(nil, "COMMAND-Nn8CRVlkeZ", "Errors.User.AlreadyExists")
|
||||
}
|
||||
events := []eventstore.Command{
|
||||
schemauser.NewCreatedEvent(ctx,
|
||||
UserV3AggregateFromWriteModel(&wm.WriteModel),
|
||||
schemaID, schemaRevision, data,
|
||||
),
|
||||
}
|
||||
if email != nil {
|
||||
emailEvents, plainCodeEmail, err := wm.NewEmailCreate(ctx,
|
||||
email,
|
||||
code,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
if plainCodeEmail != "" {
|
||||
codeEmail = plainCodeEmail
|
||||
}
|
||||
events = append(events, emailEvents...)
|
||||
}
|
||||
|
||||
if phone != nil {
|
||||
phoneEvents, plainCodePhone, err := wm.NewPhoneCreate(ctx,
|
||||
phone,
|
||||
code,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
if plainCodePhone != "" {
|
||||
codePhone = plainCodePhone
|
||||
}
|
||||
events = append(events, phoneEvents...)
|
||||
}
|
||||
|
||||
return events, codeEmail, codePhone, nil
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) getSchemaRoleForWrite(ctx context.Context, resourceOwner, userID string) (domain_schema.Role, error) {
|
||||
if userID == authz.GetCtxData(ctx).UserID {
|
||||
return domain_schema.RoleSelf, nil
|
||||
}
|
||||
if err := wm.checkPermission(ctx, domain.PermissionUserWrite, resourceOwner, userID); err != nil {
|
||||
return domain_schema.RoleUnspecified, err
|
||||
}
|
||||
return domain_schema.RoleOwner, nil
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) validateData(ctx context.Context, data []byte, schemaWM *UserSchemaWriteModel) (string, uint64, error) {
|
||||
// get role for permission check in schema through extension
|
||||
role, err := wm.getSchemaRoleForWrite(ctx, wm.ResourceOwner, wm.AggregateID)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
schema, err := domain_schema.NewSchema(role, bytes.NewReader(schemaWM.Schema))
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
// if data not changed but a new schema or revision should be used
|
||||
if data == nil {
|
||||
data = wm.Data
|
||||
}
|
||||
var v interface{}
|
||||
if err := json.Unmarshal(data, &v); err != nil {
|
||||
return "", 0, zerrors.ThrowInvalidArgument(nil, "COMMAND-7o3ZGxtXUz", "Errors.User.Invalid")
|
||||
}
|
||||
|
||||
if err := schema.Validate(v); err != nil {
|
||||
return "", 0, zerrors.ThrowPreconditionFailed(nil, "COMMAND-SlKXqLSeL6", "Errors.UserSchema.Data.Invalid")
|
||||
}
|
||||
return schemaWM.AggregateID, schemaWM.SchemaRevision, nil
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) NewUpdate(
|
||||
ctx context.Context,
|
||||
schemaWM *UserSchemaWriteModel,
|
||||
user *SchemaUser,
|
||||
email *Email,
|
||||
phone *Phone,
|
||||
code func(context.Context) (*EncryptedCode, error),
|
||||
) (_ []eventstore.Command, codeEmail string, codePhone string, err error) {
|
||||
if err := wm.checkPermissionWrite(ctx, wm.ResourceOwner, wm.AggregateID); err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
if !wm.Exists() {
|
||||
return nil, "", "", zerrors.ThrowPreconditionFailed(nil, "COMMAND-Nn8CRVlkeZ", "Errors.User.NotFound")
|
||||
}
|
||||
events := make([]eventstore.Command, 0)
|
||||
if user != nil {
|
||||
schemaID, schemaRevision, err := wm.validateData(ctx, user.Data, schemaWM)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
userEvents := wm.newUpdatedEvents(ctx,
|
||||
schemaID,
|
||||
schemaRevision,
|
||||
user.Data,
|
||||
)
|
||||
events = append(events, userEvents...)
|
||||
}
|
||||
if email != nil {
|
||||
emailEvents, plainCodeEmail, err := wm.NewEmailUpdate(ctx,
|
||||
email,
|
||||
code,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
if plainCodeEmail != "" {
|
||||
codeEmail = plainCodeEmail
|
||||
}
|
||||
events = append(events, emailEvents...)
|
||||
}
|
||||
|
||||
if phone != nil {
|
||||
phoneEvents, plainCodePhone, err := wm.NewPhoneCreate(ctx,
|
||||
phone,
|
||||
code,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
if plainCodePhone != "" {
|
||||
codePhone = plainCodePhone
|
||||
}
|
||||
events = append(events, phoneEvents...)
|
||||
}
|
||||
|
||||
return events, codeEmail, codePhone, nil
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) newUpdatedEvents(
|
||||
ctx context.Context,
|
||||
schemaID string,
|
||||
schemaRevision uint64,
|
||||
data json.RawMessage,
|
||||
) []eventstore.Command {
|
||||
changes := make([]schemauser.Changes, 0)
|
||||
if wm.SchemaID != schemaID {
|
||||
changes = append(changes, schemauser.ChangeSchemaID(schemaID))
|
||||
@@ -176,7 +381,20 @@ func (wm *UserV3WriteModel) NewUpdatedEvent(
|
||||
if len(changes) == 0 {
|
||||
return nil
|
||||
}
|
||||
return schemauser.NewUpdatedEvent(ctx, agg, changes)
|
||||
return []eventstore.Command{schemauser.NewUpdatedEvent(ctx, UserV3AggregateFromWriteModel(&wm.WriteModel), changes)}
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) NewDelete(
|
||||
ctx context.Context,
|
||||
) (_ []eventstore.Command, err error) {
|
||||
if !wm.Exists() {
|
||||
return nil, zerrors.ThrowNotFound(nil, "COMMAND-syHyCsGmvM", "Errors.User.NotFound")
|
||||
}
|
||||
if err := wm.checkPermissionDelete(ctx, wm.ResourceOwner, wm.AggregateID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []eventstore.Command{schemauser.NewDeletedEvent(ctx, UserV3AggregateFromWriteModel(&wm.WriteModel))}, nil
|
||||
|
||||
}
|
||||
|
||||
func UserV3AggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggregate {
|
||||
@@ -192,3 +410,271 @@ func UserV3AggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggreg
|
||||
func (wm *UserV3WriteModel) Exists() bool {
|
||||
return wm.State != domain.UserStateDeleted && wm.State != domain.UserStateUnspecified
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) checkPermissionWrite(
|
||||
ctx context.Context,
|
||||
resourceOwner string,
|
||||
userID string,
|
||||
) error {
|
||||
if wm.writePermissionCheck {
|
||||
return nil
|
||||
}
|
||||
if userID != "" && userID == authz.GetCtxData(ctx).UserID {
|
||||
return nil
|
||||
}
|
||||
if err := wm.checkPermission(ctx, domain.PermissionUserWrite, resourceOwner, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
wm.writePermissionCheck = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) checkPermissionDelete(
|
||||
ctx context.Context,
|
||||
resourceOwner string,
|
||||
userID string,
|
||||
) error {
|
||||
if userID != "" && userID == authz.GetCtxData(ctx).UserID {
|
||||
return nil
|
||||
}
|
||||
return wm.checkPermission(ctx, domain.PermissionUserDelete, resourceOwner, userID)
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) NewEmailCreate(
|
||||
ctx context.Context,
|
||||
email *Email,
|
||||
code func(context.Context) (*EncryptedCode, error),
|
||||
) (_ []eventstore.Command, plainCode string, err error) {
|
||||
if err := wm.checkPermissionWrite(ctx, wm.ResourceOwner, wm.AggregateID); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if email == nil || wm.Email == string(email.Address) {
|
||||
return nil, "", nil
|
||||
}
|
||||
events := []eventstore.Command{
|
||||
schemauser.NewEmailUpdatedEvent(ctx,
|
||||
UserV3AggregateFromWriteModel(&wm.WriteModel),
|
||||
email.Address,
|
||||
),
|
||||
}
|
||||
if email.Verified {
|
||||
events = append(events, wm.newEmailVerifiedEvent(ctx))
|
||||
} else {
|
||||
codeEvent, code, err := wm.newEmailCodeAddedEvent(ctx, code, email.URLTemplate, email.ReturnCode)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
events = append(events, codeEvent)
|
||||
if code != "" {
|
||||
plainCode = code
|
||||
}
|
||||
}
|
||||
return events, plainCode, nil
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) NewEmailUpdate(
|
||||
ctx context.Context,
|
||||
email *Email,
|
||||
code func(context.Context) (*EncryptedCode, error),
|
||||
) (_ []eventstore.Command, plainCode string, err error) {
|
||||
if !wm.EmailWM {
|
||||
return nil, "", nil
|
||||
}
|
||||
if !wm.Exists() {
|
||||
return nil, "", zerrors.ThrowNotFound(nil, "COMMAND-nJ0TQFuRmP", "Errors.User.NotFound")
|
||||
}
|
||||
return wm.NewEmailCreate(ctx, email, code)
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) NewEmailVerify(
|
||||
ctx context.Context,
|
||||
verify func(creationDate time.Time, expiry time.Duration, cryptoCode *crypto.CryptoValue) error,
|
||||
) ([]eventstore.Command, error) {
|
||||
if !wm.EmailWM {
|
||||
return nil, nil
|
||||
}
|
||||
if !wm.Exists() {
|
||||
return nil, zerrors.ThrowNotFound(nil, "COMMAND-qbGyMPvjvj", "Errors.User.NotFound")
|
||||
}
|
||||
if err := wm.checkPermissionWrite(ctx, wm.ResourceOwner, wm.AggregateID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if wm.EmailCode == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if err := verify(wm.EmailCode.CreationDate, wm.EmailCode.Expiry, wm.EmailCode.Code); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []eventstore.Command{wm.newEmailVerifiedEvent(ctx)}, nil
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) newEmailVerifiedEvent(
|
||||
ctx context.Context,
|
||||
) *schemauser.EmailVerifiedEvent {
|
||||
return schemauser.NewEmailVerifiedEvent(ctx, UserV3AggregateFromWriteModel(&wm.WriteModel))
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) NewResendEmailCode(
|
||||
ctx context.Context,
|
||||
code func(context.Context) (*EncryptedCode, error),
|
||||
urlTemplate string,
|
||||
isReturnCode bool,
|
||||
) (_ []eventstore.Command, plainCode string, err error) {
|
||||
if !wm.EmailWM {
|
||||
return nil, "", nil
|
||||
}
|
||||
if !wm.Exists() {
|
||||
return nil, "", zerrors.ThrowNotFound(nil, "COMMAND-EajeF6ypOV", "Errors.User.NotFound")
|
||||
}
|
||||
if err := wm.checkPermissionWrite(ctx, wm.ResourceOwner, wm.AggregateID); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if wm.EmailCode == nil {
|
||||
return nil, "", zerrors.ThrowPreconditionFailed(err, "COMMAND-QRkNTBwF8q", "Errors.User.Code.Empty")
|
||||
}
|
||||
event, plainCode, err := wm.newEmailCodeAddedEvent(ctx, code, urlTemplate, isReturnCode)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return []eventstore.Command{event}, plainCode, nil
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) newEmailCodeAddedEvent(
|
||||
ctx context.Context,
|
||||
code func(context.Context) (*EncryptedCode, error),
|
||||
urlTemplate string,
|
||||
isReturnCode bool,
|
||||
) (_ *schemauser.EmailCodeAddedEvent, plainCode string, err error) {
|
||||
cryptoCode, err := code(ctx)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if isReturnCode {
|
||||
plainCode = cryptoCode.Plain
|
||||
}
|
||||
return schemauser.NewEmailCodeAddedEvent(ctx,
|
||||
UserV3AggregateFromWriteModel(&wm.WriteModel),
|
||||
cryptoCode.Crypted,
|
||||
cryptoCode.Expiry,
|
||||
urlTemplate,
|
||||
isReturnCode,
|
||||
), plainCode, nil
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) NewPhoneCreate(
|
||||
ctx context.Context,
|
||||
phone *Phone,
|
||||
code func(context.Context) (*EncryptedCode, error),
|
||||
) (_ []eventstore.Command, plainCode string, err error) {
|
||||
if err := wm.checkPermissionWrite(ctx, wm.ResourceOwner, wm.AggregateID); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if phone == nil || wm.Phone == string(phone.Number) {
|
||||
return nil, "", nil
|
||||
}
|
||||
events := []eventstore.Command{
|
||||
schemauser.NewPhoneUpdatedEvent(ctx,
|
||||
UserV3AggregateFromWriteModel(&wm.WriteModel),
|
||||
phone.Number,
|
||||
),
|
||||
}
|
||||
if phone.Verified {
|
||||
events = append(events, wm.newPhoneVerifiedEvent(ctx))
|
||||
} else {
|
||||
codeEvent, code, err := wm.newPhoneCodeAddedEvent(ctx, code, phone.ReturnCode)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
events = append(events, codeEvent)
|
||||
if code != "" {
|
||||
plainCode = code
|
||||
}
|
||||
}
|
||||
return events, plainCode, nil
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) NewPhoneUpdate(
|
||||
ctx context.Context,
|
||||
phone *Phone,
|
||||
code func(context.Context) (*EncryptedCode, error),
|
||||
) (_ []eventstore.Command, plainCode string, err error) {
|
||||
if !wm.PhoneWM {
|
||||
return nil, "", nil
|
||||
}
|
||||
if !wm.Exists() {
|
||||
return nil, "", zerrors.ThrowNotFound(nil, "COMMAND-b33QAVgel6", "Errors.User.NotFound")
|
||||
}
|
||||
return wm.NewPhoneCreate(ctx, phone, code)
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) NewPhoneVerify(
|
||||
ctx context.Context,
|
||||
verify func(creationDate time.Time, expiry time.Duration, cryptoCode *crypto.CryptoValue) error,
|
||||
) ([]eventstore.Command, error) {
|
||||
if !wm.PhoneWM {
|
||||
return nil, nil
|
||||
}
|
||||
if !wm.Exists() {
|
||||
return nil, zerrors.ThrowNotFound(nil, "COMMAND-bx2OLtgGNS", "Errors.User.NotFound")
|
||||
}
|
||||
if err := wm.checkPermissionWrite(ctx, wm.ResourceOwner, wm.AggregateID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if wm.PhoneCode == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if err := verify(wm.PhoneCode.CreationDate, wm.PhoneCode.Expiry, wm.PhoneCode.Code); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []eventstore.Command{wm.newPhoneVerifiedEvent(ctx)}, nil
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) newPhoneVerifiedEvent(
|
||||
ctx context.Context,
|
||||
) *schemauser.PhoneVerifiedEvent {
|
||||
return schemauser.NewPhoneVerifiedEvent(ctx, UserV3AggregateFromWriteModel(&wm.WriteModel))
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) NewResendPhoneCode(
|
||||
ctx context.Context,
|
||||
code func(context.Context) (*EncryptedCode, error),
|
||||
isReturnCode bool,
|
||||
) (_ []eventstore.Command, plainCode string, err error) {
|
||||
if !wm.PhoneWM {
|
||||
return nil, "", nil
|
||||
}
|
||||
if !wm.Exists() {
|
||||
return nil, "", zerrors.ThrowNotFound(nil, "COMMAND-z8Bu9vuL9s", "Errors.User.NotFound")
|
||||
}
|
||||
if err := wm.checkPermissionWrite(ctx, wm.ResourceOwner, wm.AggregateID); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if wm.PhoneCode == nil {
|
||||
return nil, "", zerrors.ThrowPreconditionFailed(err, "COMMAND-fEsHdqECzb", "Errors.User.Code.Empty")
|
||||
}
|
||||
event, plainCode, err := wm.newPhoneCodeAddedEvent(ctx, code, isReturnCode)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return []eventstore.Command{event}, plainCode, nil
|
||||
}
|
||||
|
||||
func (wm *UserV3WriteModel) newPhoneCodeAddedEvent(
|
||||
ctx context.Context,
|
||||
code func(context.Context) (*EncryptedCode, error),
|
||||
isReturnCode bool,
|
||||
) (_ *schemauser.PhoneCodeAddedEvent, plainCode string, err error) {
|
||||
cryptoCode, err := code(ctx)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if isReturnCode {
|
||||
plainCode = cryptoCode.Plain
|
||||
}
|
||||
return schemauser.NewPhoneCodeAddedEvent(ctx,
|
||||
UserV3AggregateFromWriteModel(&wm.WriteModel),
|
||||
cryptoCode.Crypted,
|
||||
cryptoCode.Expiry,
|
||||
isReturnCode,
|
||||
), plainCode, nil
|
||||
}
|
||||
|
107
internal/command/user_v3_phone.go
Normal file
107
internal/command/user_v3_phone.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
type ChangeSchemaUserPhone struct {
|
||||
ResourceOwner string
|
||||
ID string
|
||||
|
||||
Phone *Phone
|
||||
ReturnCode *string
|
||||
}
|
||||
|
||||
func (s *ChangeSchemaUserPhone) Valid() (err error) {
|
||||
if s.ID == "" {
|
||||
return zerrors.ThrowInvalidArgument(nil, "COMMAND-DkQ9aurv5u", "Errors.IDMissing")
|
||||
}
|
||||
if s.Phone != nil && s.Phone.Number != "" {
|
||||
if s.Phone.Number, err = s.Phone.Number.Normalize(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeSchemaUserPhone(ctx context.Context, user *ChangeSchemaUserPhone) (_ *domain.ObjectDetails, err error) {
|
||||
if err := user.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
writeModel, err := c.getSchemaUserPhoneWriteModelByID(ctx, user.ResourceOwner, user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events, plainCode, err := writeModel.NewPhoneUpdate(ctx,
|
||||
user.Phone,
|
||||
func(ctx context.Context) (*EncryptedCode, error) {
|
||||
return c.newPhoneCode(ctx, c.eventstore.Filter, c.userEncryption) //nolint:staticcheck
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if plainCode != "" {
|
||||
user.ReturnCode = &plainCode
|
||||
}
|
||||
return c.pushAppendAndReduceDetails(ctx, writeModel, events...)
|
||||
}
|
||||
|
||||
func (c *Commands) VerifySchemaUserPhone(ctx context.Context, resourceOwner, id, code string) (*domain.ObjectDetails, error) {
|
||||
if id == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-R4LKY44Ke3", "Errors.IDMissing")
|
||||
}
|
||||
writeModel, err := c.getSchemaUserPhoneWriteModelByID(ctx, resourceOwner, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events, err := writeModel.NewPhoneVerify(ctx,
|
||||
func(creationDate time.Time, expiry time.Duration, cryptoCode *crypto.CryptoValue) error {
|
||||
return crypto.VerifyCode(creationDate, expiry, cryptoCode, code, c.userEncryption)
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.pushAppendAndReduceDetails(ctx, writeModel, events...)
|
||||
}
|
||||
|
||||
type ResendSchemaUserPhoneCode struct {
|
||||
ResourceOwner string
|
||||
ID string
|
||||
|
||||
ReturnCode bool
|
||||
PlainCode *string
|
||||
}
|
||||
|
||||
func (c *Commands) ResendSchemaUserPhoneCode(ctx context.Context, user *ResendSchemaUserPhoneCode) (*domain.ObjectDetails, error) {
|
||||
if user.ID == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-zmxIFR2nMo", "Errors.IDMissing")
|
||||
}
|
||||
writeModel, err := c.getSchemaUserPhoneWriteModelByID(ctx, user.ResourceOwner, user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
events, plainCode, err := writeModel.NewResendPhoneCode(ctx,
|
||||
func(ctx context.Context) (*EncryptedCode, error) {
|
||||
return c.newPhoneCode(ctx, c.eventstore.Filter, c.userEncryption) //nolint:staticcheck
|
||||
},
|
||||
user.ReturnCode,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if plainCode != "" {
|
||||
user.PlainCode = &plainCode
|
||||
}
|
||||
return c.pushAppendAndReduceDetails(ctx, writeModel, events...)
|
||||
}
|
1040
internal/command/user_v3_phone_test.go
Normal file
1040
internal/command/user_v3_phone_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
|
||||
@@ -875,8 +874,9 @@ func TestCommands_CreateSchemaUser(t *testing.T) {
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
}
|
||||
err := c.CreateSchemaUser(tt.args.ctx, tt.args.user, crypto.CreateMockEncryptionAlg(gomock.NewController(t)))
|
||||
details, err := c.CreateSchemaUser(tt.args.ctx, tt.args.user)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -884,14 +884,16 @@ func TestCommands_CreateSchemaUser(t *testing.T) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assertObjectDetails(t, tt.res.details, tt.args.user.Details)
|
||||
assertObjectDetails(t, tt.res.details, details)
|
||||
}
|
||||
|
||||
if tt.res.returnCodePhone != "" {
|
||||
assert.Equal(t, tt.res.returnCodePhone, tt.args.user.ReturnCodePhone)
|
||||
assert.NotNil(t, tt.args.user.ReturnCodePhone)
|
||||
assert.Equal(t, tt.res.returnCodePhone, *tt.args.user.ReturnCodePhone)
|
||||
}
|
||||
if tt.res.returnCodeEmail != "" {
|
||||
assert.Equal(t, tt.res.returnCodeEmail, tt.args.user.ReturnCodeEmail)
|
||||
assert.NotNil(t, tt.args.user.ReturnCodeEmail)
|
||||
assert.Equal(t, tt.res.returnCodeEmail, *tt.args.user.ReturnCodeEmail)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1988,6 +1990,19 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
"schema not existing, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
@@ -1995,8 +2010,10 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
SchemaID: gu.Ptr("type"),
|
||||
ID: "user1",
|
||||
SchemaUser: &SchemaUser{
|
||||
SchemaID: "type",
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -2060,6 +2077,25 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schema.NewAggregate("id1", "instanceID").Aggregate,
|
||||
"type",
|
||||
json.RawMessage(`{
|
||||
"$schema": "urn:zitadel:schema:v1",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}`),
|
||||
[]domain.AuthenticatorType{domain.AuthenticatorTypeUsername},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
},
|
||||
@@ -2067,9 +2103,11 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
Data: json.RawMessage(`{
|
||||
SchemaUser: &SchemaUser{
|
||||
Data: json.RawMessage(`{
|
||||
"name": "user"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -2134,9 +2172,11 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
Data: json.RawMessage(`{
|
||||
SchemaUser: &SchemaUser{
|
||||
Data: json.RawMessage(`{
|
||||
"name": "user2"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -2149,6 +2189,19 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
"user updated, changed schema",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
@@ -2168,19 +2221,6 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
schemauser.NewUpdatedEvent(
|
||||
context.Background(),
|
||||
@@ -2196,8 +2236,10 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
SchemaID: gu.Ptr("id2"),
|
||||
ID: "user1",
|
||||
SchemaUser: &SchemaUser{
|
||||
SchemaID: "id2",
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -2210,6 +2252,19 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
"user updated, new schema",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
@@ -2229,19 +2284,6 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
schemauser.NewUpdatedEvent(
|
||||
context.Background(),
|
||||
@@ -2262,11 +2304,13 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
SchemaID: gu.Ptr("id2"),
|
||||
Data: json.RawMessage(`{
|
||||
ID: "user1",
|
||||
SchemaUser: &SchemaUser{
|
||||
SchemaID: "id2",
|
||||
Data: json.RawMessage(`{
|
||||
"name": "user2"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -2350,9 +2394,11 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
Data: json.RawMessage(`{
|
||||
SchemaUser: &SchemaUser{
|
||||
Data: json.RawMessage(`{
|
||||
"name2": "user2"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -2365,6 +2411,19 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
"user updated, new schema and revision",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
2,
|
||||
json.RawMessage(`{
|
||||
"name1": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
@@ -2384,19 +2443,6 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
2,
|
||||
json.RawMessage(`{
|
||||
"name1": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
schemauser.NewUpdatedEvent(
|
||||
context.Background(),
|
||||
@@ -2418,11 +2464,13 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
SchemaID: gu.Ptr("id2"),
|
||||
Data: json.RawMessage(`{
|
||||
ID: "user1",
|
||||
SchemaUser: &SchemaUser{
|
||||
SchemaID: "id2",
|
||||
Data: json.RawMessage(`{
|
||||
"name2": "user2"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -2435,6 +2483,19 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
"user update, no field permission as admin",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
@@ -2457,30 +2518,19 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
SchemaID: gu.Ptr("id1"),
|
||||
Data: json.RawMessage(`{
|
||||
ID: "user1",
|
||||
SchemaUser: &SchemaUser{
|
||||
SchemaID: "id1",
|
||||
Data: json.RawMessage(`{
|
||||
"name": "user"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -2494,6 +2544,18 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
), expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
context.Background(),
|
||||
@@ -2515,29 +2577,18 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "org1", "user1"),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
SchemaID: gu.Ptr("type"),
|
||||
Data: json.RawMessage(`{
|
||||
ID: "user1",
|
||||
SchemaUser: &SchemaUser{
|
||||
SchemaID: "type",
|
||||
Data: json.RawMessage(`{
|
||||
"name": "user"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -2550,6 +2601,19 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
"user update, invalid data type",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
@@ -2569,30 +2633,19 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "org1", "user1"),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
SchemaID: gu.Ptr("type"),
|
||||
Data: json.RawMessage(`{
|
||||
ID: "user1",
|
||||
SchemaUser: &SchemaUser{
|
||||
SchemaID: "type",
|
||||
Data: json.RawMessage(`{
|
||||
"name": 1
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -2605,6 +2658,19 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
"user update, additional property",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
@@ -2624,19 +2690,6 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
schemauser.NewUpdatedEvent(
|
||||
context.Background(),
|
||||
@@ -2657,12 +2710,14 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
SchemaID: gu.Ptr("id1"),
|
||||
Data: json.RawMessage(`{
|
||||
ID: "user1",
|
||||
SchemaUser: &SchemaUser{
|
||||
SchemaID: "id1",
|
||||
Data: json.RawMessage(`{
|
||||
"name": "user1",
|
||||
"additional": "property"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -2675,6 +2730,19 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
"user update, invalid data attribute name",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
@@ -2695,30 +2763,19 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "org1", "user1"),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
SchemaID: gu.Ptr("type"),
|
||||
Data: json.RawMessage(`{
|
||||
ID: "user1",
|
||||
SchemaUser: &SchemaUser{
|
||||
SchemaID: "type",
|
||||
Data: json.RawMessage(`{
|
||||
"invalid": "user"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -2775,6 +2832,19 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
"user update, email return",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schema.NewCreatedEvent(
|
||||
@@ -2794,19 +2864,6 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
schemauser.NewCreatedEvent(
|
||||
context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
"id1",
|
||||
1,
|
||||
json.RawMessage(`{
|
||||
"name": "user1"
|
||||
}`),
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
schemauser.NewEmailUpdatedEvent(context.Background(),
|
||||
&schemauser.NewAggregate("user1", "org1").Aggregate,
|
||||
@@ -2832,8 +2889,10 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &ChangeSchemaUser{
|
||||
ID: "user1",
|
||||
SchemaID: gu.Ptr("id1"),
|
||||
ID: "user1",
|
||||
SchemaUser: &SchemaUser{
|
||||
SchemaID: "id1",
|
||||
},
|
||||
Email: &Email{
|
||||
Address: "test@example.com",
|
||||
ReturnCode: true,
|
||||
@@ -3101,8 +3160,9 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
userEncryption: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
}
|
||||
err := c.ChangeSchemaUser(tt.args.ctx, tt.args.user, crypto.CreateMockEncryptionAlg(gomock.NewController(t)))
|
||||
details, err := c.ChangeSchemaUser(tt.args.ctx, tt.args.user)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -3110,14 +3170,16 @@ func TestCommands_ChangeSchemaUser(t *testing.T) {
|
||||
t.Errorf("got wrong err: %v ", err)
|
||||
}
|
||||
if tt.res.err == nil {
|
||||
assertObjectDetails(t, tt.res.details, tt.args.user.Details)
|
||||
assertObjectDetails(t, tt.res.details, details)
|
||||
}
|
||||
|
||||
if tt.res.returnCodePhone != "" {
|
||||
assert.Equal(t, tt.res.returnCodePhone, tt.args.user.ReturnCodePhone)
|
||||
assert.NotNil(t, tt.args.user.ReturnCodePhone)
|
||||
assert.Equal(t, tt.res.returnCodePhone, *tt.args.user.ReturnCodePhone)
|
||||
}
|
||||
if tt.res.returnCodeEmail != "" {
|
||||
assert.Equal(t, tt.res.returnCodeEmail, tt.args.user.ReturnCodeEmail)
|
||||
assert.NotNil(t, tt.args.user.ReturnCodeEmail)
|
||||
assert.Equal(t, tt.res.returnCodeEmail, *tt.args.user.ReturnCodeEmail)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user