mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-11 00:25:12 +00:00
feat: new user auth api (#1168)
* fix: correct selectors for extended writemodel * fix: no previous checks in eventstore * start check previous * feat: auth user commands * feat: auth user commands * feat: auth user commands * feat: otp * feat: corrections from pr merge * feat: webauthn * feat: comment old webauthn * feat: refactor user, human, machine * feat: webauth command side * feat: command and query side in login * feat: fix user writemodel append events * fix: remove creation dates on command side * fix: remove previous sequence * previous sequence * fix: external idps * Update internal/api/grpc/management/user.go Co-authored-by: Livio Amstutz <[email protected]> * Update internal/v2/command/user_human_email.go Co-authored-by: Livio Amstutz <[email protected]> * fix: pr changes * fix: phone verification Co-authored-by: adlerhurst <[email protected]> Co-authored-by: Livio Amstutz <[email protected]>
This commit is contained in:
co-authored by
Livio Amstutz
adlerhurst
parent
e5731b0d3b
commit
959530ddad
+116
-110
@@ -2,14 +2,26 @@ package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
func (r *CommandSide) AddHuman(ctx context.Context, orgID, username string, human *domain.Human) (*domain.Human, error) {
|
||||
userAgg, addedHuman, err := r.addHuman(ctx, orgID, username, human)
|
||||
func (r *CommandSide) getHuman(ctx context.Context, userID, resourceowner string) (*domain.Human, error) {
|
||||
writeModel, err := r.getHumanWriteModelByID(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if writeModel.UserState == domain.UserStateUnspecified || writeModel.UserState == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-M9dsd", "Errors.User.NotFound")
|
||||
}
|
||||
return writeModelToHuman(writeModel), nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) AddHuman(ctx context.Context, orgID string, human *domain.Human) (*domain.Human, error) {
|
||||
userAgg, addedHuman, err := r.addHuman(ctx, orgID, human)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -21,10 +33,34 @@ func (r *CommandSide) AddHuman(ctx context.Context, orgID, username string, huma
|
||||
return writeModelToHuman(addedHuman), nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) addHuman(ctx context.Context, orgID, username string, human *domain.Human) (*user.Aggregate, *HumanWriteModel, error) {
|
||||
func (r *CommandSide) addHuman(ctx context.Context, orgID string, human *domain.Human) (*user.Aggregate, *HumanWriteModel, error) {
|
||||
if !human.IsValid() {
|
||||
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M90d", "Errors.User.Invalid")
|
||||
}
|
||||
return r.createHuman(ctx, orgID, human, nil, false)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RegisterHuman(ctx context.Context, orgID string, human *domain.Human, externalIDP *domain.ExternalIDP) (*domain.Human, error) {
|
||||
userAgg, addedHuman, err := r.registerHuman(ctx, orgID, human, externalIDP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return writeModelToHuman(addedHuman), nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) registerHuman(ctx context.Context, orgID string, human *domain.Human, externalIDP *domain.ExternalIDP) (*user.Aggregate, *HumanWriteModel, error) {
|
||||
if !human.IsValid() || externalIDP == nil && (human.Password == nil || human.SecretString == "") {
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-9dk45", "Errors.User.Invalid")
|
||||
}
|
||||
return r.createHuman(ctx, orgID, human, externalIDP, true)
|
||||
}
|
||||
|
||||
func (r *CommandSide) createHuman(ctx context.Context, orgID string, human *domain.Human, externalIDP *domain.ExternalIDP, selfregister bool) (*user.Aggregate, *HumanWriteModel, error) {
|
||||
userID, err := r.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -40,8 +76,8 @@ func (r *CommandSide) addHuman(ctx context.Context, orgID, username string, huma
|
||||
}
|
||||
|
||||
addedHuman := NewHumanWriteModel(human.AggregateID, orgID)
|
||||
//TODO: Check Unique Username
|
||||
if err := human.CheckOrgIAMPolicy(username, orgIAMPolicy); err != nil {
|
||||
//TODO: Check Unique Username or unique external idp
|
||||
if err := human.CheckOrgIAMPolicy(human.Username, orgIAMPolicy); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
human.SetNamesAsDisplayname()
|
||||
@@ -50,6 +86,73 @@ func (r *CommandSide) addHuman(ctx context.Context, orgID, username string, huma
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&addedHuman.WriteModel)
|
||||
var createEvent eventstore.EventPusher
|
||||
if selfregister {
|
||||
createEvent = createRegisterHumanEvent(ctx, human.Username, human)
|
||||
} else {
|
||||
createEvent = createAddHumanEvent(ctx, human.Username, human)
|
||||
}
|
||||
userAgg.PushEvents(createEvent)
|
||||
|
||||
if externalIDP != nil {
|
||||
if !externalIDP.IsValid() {
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4Dj9s", "Errors.User.ExternalIDP.Invalid")
|
||||
}
|
||||
//TODO: check if idpconfig exists
|
||||
userAgg.PushEvents(user.NewHumanExternalIDPAddedEvent(ctx, externalIDP.IDPConfigID, externalIDP.DisplayName))
|
||||
}
|
||||
if human.IsInitialState() {
|
||||
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
|
||||
}
|
||||
if human.Email != nil && human.EmailAddress != "" && human.IsEmailVerified {
|
||||
userAgg.PushEvents(user.NewHumanEmailVerifiedEvent(ctx))
|
||||
}
|
||||
if human.Phone != nil && human.PhoneNumber != "" && !human.IsPhoneVerified {
|
||||
phoneCode, err := domain.NewPhoneCode(r.phoneVerificationCode)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry)
|
||||
} else if human.Phone != nil && human.PhoneNumber != "" && human.IsPhoneVerified {
|
||||
userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx))
|
||||
}
|
||||
|
||||
return userAgg, addedHuman, nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) ResendInitialMail(ctx context.Context, userID, email, resourceowner string) (err error) {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
||||
existingEmail, err := r.emailWriteModel(ctx, userID, resourceowner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9df", "Errors.User.NotFound")
|
||||
}
|
||||
if existingEmail.UserState != domain.UserStateInitial {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.AlreadyInitialised")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
||||
if email != "" && existingEmail.Email != email {
|
||||
changedEvent, _ := existingEmail.NewChangedEvent(ctx, email)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
}
|
||||
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
|
||||
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
|
||||
}
|
||||
|
||||
func createAddHumanEvent(ctx context.Context, username string, human *domain.Human) *user.HumanAddedEvent {
|
||||
addEvent := user.NewHumanAddedEvent(
|
||||
ctx,
|
||||
username,
|
||||
@@ -75,60 +178,10 @@ func (r *CommandSide) addHuman(ctx context.Context, orgID, username string, huma
|
||||
if human.Password != nil {
|
||||
addEvent.AddPasswordData(human.SecretCrypto, human.ChangeRequired)
|
||||
}
|
||||
userAgg.PushEvents(addEvent)
|
||||
|
||||
if human.IsInitialState() {
|
||||
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry)
|
||||
}
|
||||
if human.Email != nil && human.EmailAddress != "" && human.IsEmailVerified {
|
||||
userAgg.PushEvents(user.NewHumanEmailVerifiedEvent(ctx))
|
||||
}
|
||||
if human.Phone != nil && human.PhoneNumber != "" && !human.IsPhoneVerified {
|
||||
phoneCode, err := domain.NewPhoneCode(r.phoneVerificationCode)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry)
|
||||
} else if human.Phone != nil && human.PhoneNumber != "" && human.IsPhoneVerified {
|
||||
userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx))
|
||||
}
|
||||
|
||||
return userAgg, addedHuman, nil
|
||||
return addEvent
|
||||
}
|
||||
|
||||
func (r *CommandSide) RegisterHuman(ctx context.Context, orgID, username string, human *domain.Human, externalIDP *domain.ExternalIDP) (*domain.Human, error) {
|
||||
if !human.IsValid() || externalIDP == nil && (human.Password == nil || human.SecretString == "") {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-9dk45", "Errors.User.Invalid")
|
||||
}
|
||||
userID, err := r.idGenerator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
human.AggregateID = userID
|
||||
orgIAMPolicy, err := r.getOrgIAMPolicy(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pwPolicy, err := r.GetOrgPasswordComplexityPolicy(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addedHuman := NewHumanWriteModel(human.AggregateID, orgID)
|
||||
//TODO: Check Unique Username or unique external idp
|
||||
if err := human.CheckOrgIAMPolicy(username, orgIAMPolicy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
human.SetNamesAsDisplayname()
|
||||
if err := human.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&addedHuman.WriteModel)
|
||||
func createRegisterHumanEvent(ctx context.Context, username string, human *domain.Human) *user.HumanRegisteredEvent {
|
||||
addEvent := user.NewHumanRegisteredEvent(
|
||||
ctx,
|
||||
username,
|
||||
@@ -154,61 +207,14 @@ func (r *CommandSide) RegisterHuman(ctx context.Context, orgID, username string,
|
||||
if human.Password != nil {
|
||||
addEvent.AddPasswordData(human.SecretCrypto, human.ChangeRequired)
|
||||
}
|
||||
userAgg.PushEvents(addEvent)
|
||||
//TODO: Add External IDP Event
|
||||
if human.IsInitialState() {
|
||||
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
|
||||
}
|
||||
return addEvent
|
||||
}
|
||||
|
||||
if human.Email != nil && human.EmailAddress != "" && human.IsEmailVerified {
|
||||
userAgg.PushEvents(user.NewHumanEmailVerifiedEvent(ctx))
|
||||
}
|
||||
if human.Phone != nil && human.PhoneNumber != "" && !human.IsPhoneVerified {
|
||||
phoneCode, err := domain.NewPhoneCode(r.phoneVerificationCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry))
|
||||
} else if human.Phone != nil && human.PhoneNumber != "" && human.IsPhoneVerified {
|
||||
userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx))
|
||||
}
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg)
|
||||
func (r *CommandSide) getHumanWriteModelByID(ctx context.Context, userID, resourceowner string) (*HumanWriteModel, error) {
|
||||
humanWriteModel := NewHumanWriteModel(userID, resourceowner)
|
||||
err := r.eventstore.FilterToQueryReducer(ctx, humanWriteModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return writeModelToHuman(addedHuman), nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) ResendInitialMail(ctx context.Context, userID, email, resourceOwner string) (err error) {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
||||
existingEmail, err := r.emailWriteModel(ctx, userID, resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9df", "Errors.User.NotFound")
|
||||
}
|
||||
if existingEmail.UserState != domain.UserStateInitial {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.AlreadyInitialised")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
||||
if email != "" && existingEmail.Email != email {
|
||||
changedEvent, _ := existingEmail.NewChangedEvent(ctx, email)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
}
|
||||
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
|
||||
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
|
||||
return humanWriteModel, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user