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:
Silvan
2020-05-13 14:22:29 +02:00
committed by GitHub
parent 7facd78026
commit 9e32740eb8
67 changed files with 16694 additions and 13035 deletions

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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))