mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:37:32 +00:00
feat: org command sides (#96)
* start org * refactor(eventstore): filter in sql for querier * feat(eventstore): Aggregate precondition preconditions are checked right before insert. Insert is still transaction save * feat(eventstore): check preconditions in repository * test(eventstore): test precondition in models * test(eventstore): precondition-tests * start org * refactor(eventstore): filter in sql for querier * feat(eventstore): Aggregate precondition preconditions are checked right before insert. Insert is still transaction save * feat(admin): start implement org * feat(eventstore): check preconditions in repository * fix(eventstore): data as NULL if empty refactor(eventstore): naming in sequence methods * feat(admin): org command side * feat(management): start org-repo * feat(org): member * fix: replace ObjectRoot.ID with ObjectRoot.AggregateID * aggregateID * add remove,change member * refactor(org): namings * refactor(eventstore): querier as type * fix(precondition): rename validation from precondition to validation * test(eventstore): isErr func instead of wantErr bool * fix(tests): Data * fix(eventstore): correct check for existing events in push, simplify insert statement * fix(eventstore): aggregate id public * test(org): eventsourcing * test(org): eventstore * test(org): deactivate, reactivate, orgbyid * test(org): getMemberByIDs * tests * running tests * add user repo to admin * thorw not found if no org found * eventstore tests done * lauft * validate if user is already member of org * modules * delete unused file * add member validation test * return error if unable to validat member * generate org id once, set resourceowner of org * Update internal/admin/repository/eventsourcing/eventstore/org.go * Update internal/admin/repository/eventsourcing/eventstore/org.go * Update internal/org/repository/eventsourcing/member_model.go * Update internal/org/repository/eventsourcing/org.go * Update internal/org/repository/eventsourcing/org.go * Update internal/org/repository/eventsourcing/org_member.go * Update internal/org/repository/eventsourcing/org_member.go * Update internal/org/repository/eventsourcing/org_model.go * Update internal/org/repository/eventsourcing/org.go * Update internal/org/repository/eventsourcing/org_model.go * Update internal/org/repository/eventsourcing/org_model.go * typo * correct user events * usercreate for setuporg instead of userregister * set data * mod * mod * tests * cleanup code * code styling * return member on add and change * change username in startup * girignore * orgID as parameter in re-/deactive org * startup config * migration for admin_api-user * probes fro admin * move unique org Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
@@ -92,26 +93,26 @@ func (u *User) HashPasswordIfExisting(passwordAlg crypto.HashAlgorithm, onetime
|
||||
}
|
||||
|
||||
func (u *User) GenerateInitCodeIfNeeded(initGenerator crypto.Generator) error {
|
||||
u.InitCode = new(InitUserCode)
|
||||
if !u.IsInitialState() {
|
||||
return nil
|
||||
}
|
||||
u.InitCode = new(InitUserCode)
|
||||
return u.InitCode.GenerateInitUserCode(initGenerator)
|
||||
}
|
||||
|
||||
func (u *User) GeneratePhoneCodeIfNeeded(phoneGenerator crypto.Generator) error {
|
||||
u.PhoneCode = new(PhoneCode)
|
||||
if u.Phone == nil || u.IsPhoneVerified {
|
||||
return nil
|
||||
}
|
||||
u.PhoneCode = new(PhoneCode)
|
||||
return u.PhoneCode.GeneratePhoneCode(phoneGenerator)
|
||||
}
|
||||
|
||||
func (u *User) GenerateEmailCodeIfNeeded(emailGenerator crypto.Generator) error {
|
||||
u.EmailCode = new(EmailCode)
|
||||
if u.Email == nil || u.IsEmailVerified {
|
||||
return nil
|
||||
}
|
||||
u.EmailCode = new(EmailCode)
|
||||
return u.EmailCode.GenerateEmailCode(emailGenerator)
|
||||
}
|
||||
|
||||
|
@@ -2,18 +2,20 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/caos/zitadel/internal/cache/config"
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
es_int "github.com/caos/zitadel/internal/eventstore"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
|
||||
global_model "github.com/caos/zitadel/internal/model"
|
||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/sony/sonyflake"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type UserEventstore struct {
|
||||
@@ -49,6 +51,7 @@ func StartUser(conf UserConfig, systemDefaults sd.SystemDefaults) (*UserEventsto
|
||||
phoneVerificationCode := crypto.NewEncryptionGenerator(systemDefaults.SecretGenerators.PhoneVerificationCode, aesCrypto)
|
||||
passwordVerificationCode := crypto.NewEncryptionGenerator(systemDefaults.SecretGenerators.PasswordVerificationCode, aesCrypto)
|
||||
aesOtpCrypto, err := crypto.NewAESCrypto(systemDefaults.Multifactors.OTP.VerificationKey)
|
||||
passwordAlg := crypto.NewBCrypt(systemDefaults.SecretGenerators.PasswordSaltCost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -67,6 +70,7 @@ func StartUser(conf UserConfig, systemDefaults sd.SystemDefaults) (*UserEventsto
|
||||
PhoneVerificationCode: phoneVerificationCode,
|
||||
PasswordVerificationCode: passwordVerificationCode,
|
||||
Multifactors: mfa,
|
||||
PasswordAlg: passwordAlg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -85,37 +89,47 @@ func (es *UserEventstore) UserByID(ctx context.Context, id string) (*usr_model.U
|
||||
return model.UserToModel(user), nil
|
||||
}
|
||||
|
||||
func (es *UserEventstore) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) {
|
||||
func (es *UserEventstore) PrepareCreateUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*model.User, *es_models.Aggregate, error) {
|
||||
user.SetEmailAsUsername()
|
||||
if !user.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Name is required")
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Name is required")
|
||||
}
|
||||
//TODO: Check Uniqueness
|
||||
id, err := es.idGenerator.NextID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
user.AggregateID = strconv.FormatUint(id, 10)
|
||||
|
||||
err = user.HashPasswordIfExisting(es.PasswordAlg, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
err = user.GenerateInitCodeIfNeeded(es.InitializeUserCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
err = user.GeneratePhoneCodeIfNeeded(es.PhoneVerificationCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
repoUser := model.UserFromModel(user)
|
||||
repoInitCode := model.InitCodeFromModel(user.InitCode)
|
||||
repoPhoneCode := model.PhoneCodeFromModel(user.PhoneCode)
|
||||
|
||||
createAggregate := UserCreateAggregate(es.AggregateCreator(), repoUser, repoInitCode, repoPhoneCode)
|
||||
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, createAggregate)
|
||||
createAggregate, err := UserCreateAggregate(ctx, es.AggregateCreator(), repoUser, repoInitCode, repoPhoneCode, resourceOwner)
|
||||
|
||||
return repoUser, createAggregate, err
|
||||
}
|
||||
|
||||
func (es *UserEventstore) CreateUser(ctx context.Context, user *usr_model.User) (*usr_model.User, error) {
|
||||
repoUser, aggregate, err := es.PrepareCreateUser(ctx, user, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoUser.AppendEvents, aggregate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -124,32 +138,41 @@ func (es *UserEventstore) CreateUser(ctx context.Context, user *usr_model.User)
|
||||
return model.UserToModel(repoUser), nil
|
||||
}
|
||||
|
||||
func (es *UserEventstore) RegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*usr_model.User, error) {
|
||||
func (es *UserEventstore) PrepareRegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*model.User, *es_models.Aggregate, error) {
|
||||
user.SetEmailAsUsername()
|
||||
if !user.IsValid() || user.Password == nil || user.SecretString == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "user is invalid")
|
||||
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "user is invalid")
|
||||
}
|
||||
//TODO: Check Uniqueness
|
||||
id, err := es.idGenerator.NextID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
user.AggregateID = strconv.FormatUint(id, 10)
|
||||
|
||||
err = user.HashPasswordIfExisting(es.PasswordAlg, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
err = user.GenerateEmailCodeIfNeeded(es.EmailVerificationCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
repoUser := model.UserFromModel(user)
|
||||
repoEmailCode := model.EmailCodeFromModel(user.EmailCode)
|
||||
|
||||
createAggregate := UserRegisterAggregate(es.AggregateCreator(), repoUser, resourceOwner, repoEmailCode)
|
||||
err = es_sdk.Push(ctx, es.PushAggregates, repoUser.AppendEvents, createAggregate)
|
||||
aggregate, err := UserRegisterAggregate(ctx, es.AggregateCreator(), repoUser, resourceOwner, repoEmailCode)
|
||||
return repoUser, aggregate, err
|
||||
}
|
||||
|
||||
func (es *UserEventstore) RegisterUser(ctx context.Context, user *usr_model.User, resourceOwner string) (*usr_model.User, error) {
|
||||
repoUser, createAggregate, err := es.PrepareRegisterUser(ctx, user, resourceOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoUser.AppendEvents, createAggregate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -2,12 +2,13 @@ package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/user/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -22,11 +23,11 @@ type User struct {
|
||||
*Email
|
||||
*Phone
|
||||
*Address
|
||||
InitCode *InitUserCode
|
||||
EmailCode *EmailCode
|
||||
PhoneCode *PhoneCode
|
||||
PasswordCode *PasswordCode
|
||||
OTP *OTP
|
||||
InitCode *InitUserCode `json:"-"`
|
||||
EmailCode *EmailCode `json:"-"`
|
||||
PhoneCode *PhoneCode `json:"-"`
|
||||
PasswordCode *PasswordCode `json:"-"`
|
||||
OTP *OTP `json:"-"`
|
||||
}
|
||||
|
||||
type InitUserCode struct {
|
||||
|
@@ -2,6 +2,7 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||
@@ -36,72 +37,72 @@ func UserAggregateOverwriteContext(ctx context.Context, aggCreator *es_models.Ag
|
||||
return aggCreator.NewAggregate(ctx, user.AggregateID, model.UserAggregate, model.UserVersion, user.Sequence, es_models.OverwriteResourceOwner(resourceOwnerID), es_models.OverwriteEditorUser(userID))
|
||||
}
|
||||
|
||||
func UserCreateAggregate(aggCreator *es_models.AggregateCreator, user *model.User, initCode *model.InitUserCode, phoneCode *model.PhoneCode) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
if user == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-duxk2", "user should not be nil")
|
||||
}
|
||||
|
||||
agg, err := UserAggregate(ctx, aggCreator, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
agg, err = agg.AppendEvent(model.UserAdded, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user.Email != nil && user.EmailAddress != "" && user.IsEmailVerified {
|
||||
agg, err = agg.AppendEvent(model.UserEmailVerified, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if user.Phone != nil && user.PhoneNumber != "" && user.IsPhoneVerified {
|
||||
agg, err = agg.AppendEvent(model.UserPhoneVerified, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if user.Password != nil {
|
||||
agg, err = agg.AppendEvent(model.UserPasswordCodeAdded, user.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if initCode != nil {
|
||||
agg, err = agg.AppendEvent(model.InitializedUserCodeAdded, initCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if phoneCode != nil {
|
||||
agg, err = agg.AppendEvent(model.UserPhoneCodeAdded, phoneCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return agg, err
|
||||
func UserCreateAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, user *model.User, initCode *model.InitUserCode, phoneCode *model.PhoneCode, resourceOwner string) (agg *es_models.Aggregate, err error) {
|
||||
if user == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-duxk2", "user should not be nil")
|
||||
}
|
||||
|
||||
if resourceOwner != "" {
|
||||
agg, err = UserAggregateOverwriteContext(ctx, aggCreator, user, user.AggregateID, resourceOwner)
|
||||
} else {
|
||||
agg, err = UserAggregate(ctx, aggCreator, user)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
agg, err = agg.AppendEvent(model.UserAdded, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user.Email != nil && user.EmailAddress != "" && user.IsEmailVerified {
|
||||
agg, err = agg.AppendEvent(model.UserEmailVerified, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if user.Phone != nil && user.PhoneNumber != "" && user.IsPhoneVerified {
|
||||
agg, err = agg.AppendEvent(model.UserPhoneVerified, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if user.Password != nil {
|
||||
agg, err = agg.AppendEvent(model.UserPasswordCodeAdded, user.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if initCode != nil {
|
||||
agg, err = agg.AppendEvent(model.InitializedUserCodeAdded, initCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if phoneCode != nil {
|
||||
agg, err = agg.AppendEvent(model.UserPhoneCodeAdded, phoneCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return agg, err
|
||||
}
|
||||
|
||||
func UserRegisterAggregate(aggCreator *es_models.AggregateCreator, user *model.User, resourceOwner string, emailCode *model.EmailCode) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
if user == nil || resourceOwner == "" || emailCode == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-duxk2", "user, resourceowner, emailcode should not be nothing")
|
||||
}
|
||||
|
||||
agg, err := UserAggregateOverwriteContext(ctx, aggCreator, user, resourceOwner, user.AggregateID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
agg, err = agg.AppendEvent(model.UserRegistered, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return agg.AppendEvent(model.UserEmailCodeAdded, emailCode)
|
||||
func UserRegisterAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, user *model.User, resourceOwner string, emailCode *model.EmailCode) (*es_models.Aggregate, error) {
|
||||
if user == nil || resourceOwner == "" || emailCode == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-duxk2", "user, resourceowner, emailcode should not be nothing")
|
||||
}
|
||||
|
||||
agg, err := UserAggregateOverwriteContext(ctx, aggCreator, user, resourceOwner, user.AggregateID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
agg, err = agg.AppendEvent(model.UserRegistered, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return agg.AppendEvent(model.UserEmailCodeAdded, emailCode)
|
||||
}
|
||||
|
||||
func UserDeactivateAggregate(aggCreator *es_models.AggregateCreator, user *model.User) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
|
@@ -2,12 +2,13 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/auth"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestUserByIDQuery(t *testing.T) {
|
||||
@@ -212,7 +213,7 @@ func TestUserCreateAggregate(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := UserCreateAggregate(tt.args.aggCreator, tt.args.new, tt.args.initCode, tt.args.phoneCode)(tt.args.ctx)
|
||||
agg, err := UserCreateAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.new, tt.args.initCode, tt.args.phoneCode, "")
|
||||
|
||||
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
@@ -329,7 +330,7 @@ func TestUserRegisterAggregate(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := UserRegisterAggregate(tt.args.aggCreator, tt.args.new, tt.args.resourceOwner, tt.args.emailCode)(tt.args.ctx)
|
||||
agg, err := UserRegisterAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.new, tt.args.resourceOwner, tt.args.emailCode)
|
||||
|
||||
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))
|
||||
|
Reference in New Issue
Block a user