zitadel/internal/command/user_v3.go
2024-10-02 11:38:16 +02:00

260 lines
6.8 KiB
Go

package command
import (
"context"
"encoding/json"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/zerrors"
)
type CreateSchemaUser struct {
ResourceOwner string
ID string
SchemaID string
Data json.RawMessage
Email *Email
ReturnCodeEmail *string
Phone *Phone
ReturnCodePhone *string
Usernames []*Username
Password *SchemaUserPassword
PublicKeys []*PublicKey
PATs []*PAT
}
func (s *CreateSchemaUser) Valid() (err error) {
if s.ResourceOwner == "" {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-urEJKa1tJM", "Errors.ResourceOwnerMissing")
}
if s.SchemaID == "" {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-TFo06JgnF2", "Errors.UserSchema.ID.Missing")
}
if s.Email != nil && s.Email.Address != "" {
if err := s.Email.Validate(); err != nil {
return err
}
}
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) CreateSchemaUser(ctx context.Context, user *CreateSchemaUser) (_ *domain.ObjectDetails, err error) {
if err := user.Valid(); err != nil {
return nil, err
}
if user.ID == "" {
user.ID, err = c.idGenerator.Next()
if err != nil {
return nil, err
}
}
writeModel, err := c.getSchemaUserWMForState(ctx, user.ResourceOwner, user.ID)
if err != nil {
return nil, err
}
schemaWriteModel, err := existingSchema(ctx, c, "", user.SchemaID)
if err != nil {
return nil, err
}
events, codeEmail, codePhone, err := writeModel.NewCreate(ctx,
schemaWriteModel,
user.Data,
user.Email,
user.Phone,
func(ctx context.Context) (*EncryptedCode, error) {
return c.newEmailCode(ctx, c.eventstore.Filter, c.userEncryption) //nolint:staticcheck
},
func(ctx context.Context) (*EncryptedCode, string, error) {
return c.newPhoneCode(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyPhoneCode, c.userEncryption, c.defaultSecretGenerators.PhoneVerificationCode) //nolint:staticcheck
},
)
if err != nil {
return nil, err
}
if codeEmail != "" {
user.ReturnCodeEmail = &codeEmail
}
if codePhone != "" {
user.ReturnCodePhone = &codePhone
}
for i := range user.Usernames {
_, usernameEvents, err := c.addUsername(ctx, writeModel.ResourceOwner, writeModel.AggregateID, user.Usernames[i])
if err != nil {
return nil, err
}
events = append(events, usernameEvents...)
}
if user.Password != nil {
_, pwEvents, err := c.setSchemaUserPassword(ctx, writeModel.ResourceOwner, writeModel.AggregateID, nil, user.Password)
if err != nil {
return nil, err
}
events = append(events, pwEvents...)
}
for i := range user.PublicKeys {
_, pkEvents, err := c.addPublicKey(ctx, writeModel.ResourceOwner, writeModel.AggregateID, user.PublicKeys[i])
if err != nil {
return nil, err
}
events = append(events, pkEvents...)
}
for i := range user.PATs {
_, patEvents, err := c.addPAT(ctx, writeModel.ResourceOwner, writeModel.AggregateID, user.PATs[i])
if err != nil {
return nil, err
}
events = append(events, patEvents...)
}
return c.pushAppendAndReduceDetails(ctx, writeModel, events...)
}
func (c *Commands) DeleteSchemaUser(ctx context.Context, resourceOwner, id string) (*domain.ObjectDetails, error) {
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-Vs4wJCME7T", "Errors.IDMissing")
}
writeModel, err := c.getSchemaUserWMForState(ctx, resourceOwner, id)
if err != nil {
return nil, err
}
events, err := writeModel.NewDelete(ctx)
if err != nil {
return nil, err
}
return c.pushAppendAndReduceDetails(ctx, writeModel, events...)
}
type ChangeSchemaUser struct {
schemaWriteModel *UserSchemaWriteModel
ResourceOwner string
ID string
SchemaUser *SchemaUser
Email *Email
ReturnCodeEmail *string
Phone *Phone
ReturnCodePhone *string
}
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.Email != nil && s.Email.Address != "" {
if err := s.Email.Validate(); err != nil {
return err
}
}
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) ChangeSchemaUser(ctx context.Context, user *ChangeSchemaUser) (*domain.ObjectDetails, error) {
if err := user.Valid(); err != nil {
return nil, err
}
writeModel, err := c.getSchemaUserWMByID(ctx, user.ResourceOwner, user.ID)
if err != nil {
return nil, err
}
// use already used schemaID, if no new schemaID is defined
schemaID := writeModel.SchemaID
if user.SchemaUser != nil && user.SchemaUser.SchemaID != "" {
schemaID = user.SchemaUser.SchemaID
}
var schemaWM *UserSchemaWriteModel
if user.SchemaUser != nil {
schemaWriteModel, err := existingSchema(ctx, c, "", schemaID)
if err != nil {
return nil, err
}
schemaWM = schemaWriteModel
}
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
},
func(ctx context.Context) (*EncryptedCode, string, error) {
return c.newPhoneCode(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyPhoneCode, c.userEncryption, c.defaultSecretGenerators.PhoneVerificationCode) //nolint:staticcheck
},
)
if err != nil {
return nil, err
}
if codeEmail != "" {
user.ReturnCodeEmail = &codeEmail
}
if codePhone != "" {
user.ReturnCodePhone = &codePhone
}
return c.pushAppendAndReduceDetails(ctx, writeModel, events...)
}
func (c *Commands) getSchemaUserWMByID(ctx context.Context, resourceOwner, id string) (*UserV3WriteModel, error) {
writeModel := NewUserV3WriteModel(resourceOwner, id, c.checkPermission)
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
return nil, err
}
return writeModel, nil
}
func existingSchema(ctx context.Context, c *Commands, resourceOwner, id string) (*UserSchemaWriteModel, error) {
writeModel, err := c.getSchemaWriteModelByID(ctx, resourceOwner, id)
if err != nil {
return nil, err
}
if !writeModel.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-VLDTtxT3If", "Errors.UserSchema.NotExists")
}
return writeModel, nil
}
func existingSchemaWithAuthenticator(ctx context.Context, c *Commands, resourceOwner, id string, authenticator domain.AuthenticatorType) (*UserSchemaWriteModel, error) {
writeModel, err := c.getSchemaWriteModelByID(ctx, resourceOwner, id)
if err != nil {
return nil, err
}
if !writeModel.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-VLDTtxT3If", "Errors.UserSchema.NotExists")
}
return writeModel, nil
}