fix: commandside queries (#1313)

* fix: move user by id to query side

* fix: move get passwordless to query side

# Conflicts:
#	internal/user/repository/eventsourcing/eventstore.go

* fix: move get passwordless to query side

* remove user eventstore

* remove unused models

* org changes

* org changes

* fix: move org queries to query side

* fix: remove org eventstore

* fix: remove org eventstore

* fix: remove org eventstore

* remove project from es v1

* project cleanup

* project cleanup

* fix: remove org eventstore

* fix: remove iam eventstore

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Fabi
2021-02-22 14:08:47 +01:00
committed by GitHub
parent 2ba56595b1
commit 428ef4acdb
106 changed files with 2301 additions and 2799 deletions

View File

@@ -1,35 +0,0 @@
package eventsourcing
import (
"github.com/caos/logging"
"github.com/caos/zitadel/internal/cache"
"github.com/caos/zitadel/internal/cache/config"
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
)
type UserCache struct {
userCache cache.Cache
}
func StartCache(conf *config.CacheConfig) (*UserCache, error) {
userCache, err := conf.Config.NewCache()
logging.Log("EVENT-vJG2j").OnError(err).Panic("unable to create user cache")
return &UserCache{userCache: userCache}, nil
}
func (c *UserCache) getUser(id string) *model.User {
user := &model.User{ObjectRoot: models.ObjectRoot{AggregateID: id}}
if err := c.userCache.Get(id, user); err != nil {
logging.Log("EVENT-AtS0S").WithError(err).Debug("error in getting cache")
}
return user
}
func (c *UserCache) cacheUser(user *model.User) {
err := c.userCache.Set(user.AggregateID, user)
if err != nil {
logging.Log("EVENT-0V2gX").WithError(err).Debug("error in setting project cache")
}
}

View File

@@ -1,143 +0,0 @@
package eventsourcing
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/cache/config"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/errors"
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"
usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
"github.com/golang/protobuf/ptypes"
)
type UserEventstore struct {
es_int.Eventstore
userCache *UserCache
}
type UserConfig struct {
es_int.Eventstore
Cache *config.CacheConfig
PasswordSaltCost int
}
func StartUser(conf UserConfig, systemDefaults sd.SystemDefaults) (*UserEventstore, error) {
userCache, err := StartCache(conf.Cache)
if err != nil {
return nil, err
}
return &UserEventstore{
Eventstore: conf.Eventstore,
userCache: userCache,
}, nil
}
func (es *UserEventstore) UserByID(ctx context.Context, id string) (*usr_model.User, error) {
user := es.userCache.getUser(id)
query, err := UserByIDQuery(user.AggregateID, user.Sequence)
if err != nil {
return nil, err
}
err = es_sdk.Filter(ctx, es.FilterEvents, user.AppendEvents, query)
if err != nil && caos_errs.IsNotFound(err) && user.Sequence == 0 {
return nil, err
}
if user.State == int32(usr_model.UserStateDeleted) {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-6hsK9", "Errors.User.NotFound")
}
es.userCache.cacheUser(user)
return model.UserToModel(user), nil
}
func (es *UserEventstore) HumanByID(ctx context.Context, userID string) (*usr_model.User, error) {
if userID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-3M9sf", "Errors.User.UserIDMissing")
}
user, err := es.UserByID(ctx, userID)
if err != nil {
return nil, err
}
if user.Human == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-jLHYG", "Errors.User.NotHuman")
}
return user, nil
}
func (es *UserEventstore) UserEventsByID(ctx context.Context, id string, sequence uint64) ([]*es_models.Event, error) {
query, err := UserByIDQuery(id, sequence)
if err != nil {
return nil, err
}
return es.FilterEvents(ctx, query)
}
func (es *UserEventstore) UserChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*usr_model.UserChanges, error) {
query := ChangesQuery(id, lastSequence, limit, sortAscending)
events, err := es.Eventstore.FilterEvents(ctx, query)
if err != nil {
logging.Log("EVENT-g9HCv").WithError(err).Warn("eventstore unavailable")
return nil, errors.ThrowInternal(err, "EVENT-htuG9", "Errors.Internal")
}
if len(events) == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-6cAxe", "Errors.User.NoChanges")
}
result := make([]*usr_model.UserChange, len(events))
for i, event := range events {
creationDate, err := ptypes.TimestampProto(event.CreationDate)
logging.Log("EVENT-8GTGS").OnError(err).Debug("unable to parse timestamp")
change := &usr_model.UserChange{
ChangeDate: creationDate,
EventType: event.Type.String(),
ModifierID: event.EditorUser,
Sequence: event.Sequence,
}
//TODO: now all types should be unmarshalled, e.g. password
// if len(event.Data) != 0 {
// user := new(model.User)
// err := json.Unmarshal(event.Data, user)
// logging.Log("EVENT-Rkg7X").OnError(err).Debug("unable to unmarshal data")
// change.Data = user
// }
result[i] = change
if lastSequence < event.Sequence {
lastSequence = event.Sequence
}
}
return &usr_model.UserChanges{
Changes: result,
LastSequence: lastSequence,
}, nil
}
func ChangesQuery(userID string, latestSequence, limit uint64, sortAscending bool) *es_models.SearchQuery {
query := es_models.NewSearchQuery().
AggregateTypeFilter(model.UserAggregate)
if !sortAscending {
query.OrderDesc()
}
query.LatestSequenceFilter(latestSequence).
AggregateIDFilter(userID).
SetLimit(limit)
return query
}
func (es *UserEventstore) GetPasswordless(ctx context.Context, userID string) ([]*usr_model.WebAuthNToken, error) {
user, err := es.HumanByID(ctx, userID)
if err != nil {
return nil, err
}
return user.PasswordlessTokens, nil
}

View File

@@ -21,22 +21,6 @@ type OTPVerified struct {
UserAgentID string `json:"userAgentID,omitempty"`
}
func OTPFromModel(otp *model.OTP) *OTP {
return &OTP{
ObjectRoot: otp.ObjectRoot,
Secret: otp.Secret,
State: int32(otp.State),
}
}
func OTPToModel(otp *OTP) *model.OTP {
return &model.OTP{
ObjectRoot: otp.ObjectRoot,
Secret: otp.Secret,
State: model.MFAState(otp.State),
}
}
func (u *Human) appendOTPAddedEvent(event *es_models.Event) error {
u.OTP = &OTP{
State: int32(model.MFAStateNotReady),

View File

@@ -24,42 +24,6 @@ type User struct {
*Machine
}
func UserFromModel(user *model.User) *User {
var human *Human
if user.Human != nil {
human = HumanFromModel(user.Human)
}
var machine *Machine
if user.Machine != nil {
machine = MachineFromModel(user.Machine)
}
return &User{
ObjectRoot: user.ObjectRoot,
State: int32(user.State),
UserName: user.UserName,
Human: human,
Machine: machine,
}
}
func UserToModel(user *User) *model.User {
var human *model.Human
if user.Human != nil {
human = HumanToModel(user.Human)
}
var machine *model.Machine
if user.Machine != nil {
machine = MachineToModel(user.Machine)
}
return &model.User{
ObjectRoot: user.ObjectRoot,
State: model.UserState(user.State),
UserName: user.UserName,
Human: human,
Machine: machine,
}
}
func (u *User) AppendEvents(events ...*es_models.Event) error {
for _, event := range events {
if err := u.AppendEvent(event); err != nil {

View File

@@ -37,113 +37,6 @@ type InitUserCode struct {
Expiry time.Duration `json:"expiry,omitempty"`
}
func HumanFromModel(user *model.Human) *Human {
human := new(Human)
if user.Password != nil {
human.Password = PasswordFromModel(user.Password)
}
if user.Profile != nil {
human.Profile = ProfileFromModel(user.Profile)
}
if user.Email != nil {
human.Email = EmailFromModel(user.Email)
}
if user.Phone != nil {
human.Phone = PhoneFromModel(user.Phone)
}
if user.Address != nil {
human.Address = AddressFromModel(user.Address)
}
if user.OTP != nil {
human.OTP = OTPFromModel(user.OTP)
}
if user.ExternalIDPs != nil {
human.ExternalIDPs = ExternalIDPsFromModel(user.ExternalIDPs)
}
if user.U2FTokens != nil {
human.U2FTokens = WebAuthNsFromModel(user.U2FTokens)
}
if user.PasswordlessTokens != nil {
human.PasswordlessTokens = WebAuthNsFromModel(user.PasswordlessTokens)
}
if user.U2FLogins != nil {
human.U2FLogins = WebAuthNLoginsFromModel(user.U2FLogins)
}
if user.PasswordlessLogins != nil {
human.PasswordlessLogins = WebAuthNLoginsFromModel(user.PasswordlessLogins)
}
return human
}
func HumanToModel(user *Human) *model.Human {
human := new(model.Human)
if user.Password != nil {
human.Password = PasswordToModel(user.Password)
}
if user.Profile != nil {
human.Profile = ProfileToModel(user.Profile)
}
if user.Email != nil {
human.Email = EmailToModel(user.Email)
}
if user.Phone != nil {
human.Phone = PhoneToModel(user.Phone)
}
if user.Address != nil {
human.Address = AddressToModel(user.Address)
}
if user.ExternalIDPs != nil {
human.ExternalIDPs = ExternalIDPsToModel(user.ExternalIDPs)
}
if user.InitCode != nil {
human.InitCode = InitCodeToModel(user.InitCode)
}
if user.EmailCode != nil {
human.EmailCode = EmailCodeToModel(user.EmailCode)
}
if user.PhoneCode != nil {
human.PhoneCode = PhoneCodeToModel(user.PhoneCode)
}
if user.PasswordCode != nil {
human.PasswordCode = PasswordCodeToModel(user.PasswordCode)
}
if user.OTP != nil {
human.OTP = OTPToModel(user.OTP)
}
if user.U2FTokens != nil {
human.U2FTokens = WebAuthNsToModel(user.U2FTokens)
}
if user.PasswordlessTokens != nil {
human.PasswordlessTokens = WebAuthNsToModel(user.PasswordlessTokens)
}
if user.U2FLogins != nil {
human.U2FLogins = WebAuthNLoginsToModel(user.U2FLogins)
}
if user.PasswordlessLogins != nil {
human.PasswordlessLogins = WebAuthNLoginsToModel(user.PasswordlessLogins)
}
return human
}
func InitCodeFromModel(code *model.InitUserCode) *InitUserCode {
if code == nil {
return nil
}
return &InitUserCode{
ObjectRoot: code.ObjectRoot,
Expiry: code.Expiry,
Code: code.Code,
}
}
func InitCodeToModel(code *InitUserCode) *model.InitUserCode {
return &model.InitUserCode{
ObjectRoot: code.ObjectRoot,
Expiry: code.Expiry,
Code: code.Code,
}
}
func (p *Human) AppendEvents(events ...*es_models.Event) error {
for _, event := range events {
if err := p.AppendEvent(event); err != nil {

View File

@@ -1,44 +0,0 @@
package model
import (
"encoding/json"
"testing"
"time"
es_models "github.com/caos/zitadel/internal/eventstore/models"
)
func TestAppendInitUserCodeEvent(t *testing.T) {
type args struct {
user *Human
code *InitUserCode
event *es_models.Event
}
tests := []struct {
name string
args args
result *Human
}{
{
name: "append init user code event",
args: args{
user: &Human{},
code: &InitUserCode{Expiry: time.Hour * 30},
event: &es_models.Event{},
},
result: &Human{InitCode: &InitUserCode{Expiry: time.Hour * 30}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.code != nil {
data, _ := json.Marshal(tt.args.code)
tt.args.event.Data = data
}
tt.args.user.appendInitUsercodeCreatedEvent(tt.args.event)
if tt.args.user.InitCode.Expiry != tt.result.InitCode.Expiry {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
}
})
}
}

View File

@@ -5,12 +5,9 @@ import (
"time"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
es_models "github.com/caos/zitadel/internal/eventstore/models"
key_model "github.com/caos/zitadel/internal/key/model"
"github.com/caos/zitadel/internal/user/model"
)
type Machine struct {
@@ -46,28 +43,6 @@ func (sa *Machine) setData(event *models.Event) error {
return nil
}
func (sa *Machine) Changes(updatedAccount *Machine) map[string]interface{} {
changes := make(map[string]interface{})
if updatedAccount.Description != "" && updatedAccount.Description != sa.Description {
changes["description"] = updatedAccount.Description
}
return changes
}
func MachineFromModel(machine *model.Machine) *Machine {
return &Machine{
Description: machine.Description,
Name: machine.Name,
}
}
func MachineToModel(machine *Machine) *model.Machine {
return &model.Machine{
Description: machine.Description,
Name: machine.Name,
}
}
type MachineKey struct {
es_models.ObjectRoot `json:"-"`
KeyID string `json:"keyId,omitempty"`
@@ -100,35 +75,3 @@ func (key *MachineKey) AppendEvent(event *es_models.Event) (err error) {
}
return err
}
func MachineKeyFromModel(machine *model.MachineKey) *MachineKey {
return &MachineKey{
ObjectRoot: machine.ObjectRoot,
ExpirationDate: machine.ExpirationDate,
KeyID: machine.KeyID,
Type: int32(machine.Type),
}
}
func MachineKeyToModel(machine *MachineKey) *model.MachineKey {
return &model.MachineKey{
ObjectRoot: machine.ObjectRoot,
ExpirationDate: machine.ExpirationDate,
KeyID: machine.KeyID,
PrivateKey: machine.privateKey,
Type: key_model.AuthNKeyType(machine.Type),
}
}
func (key *MachineKey) GenerateMachineKeyPair(keySize int, alg crypto.EncryptionAlgorithm) error {
privateKey, publicKey, err := crypto.GenerateKeyPair(keySize)
if err != nil {
return err
}
key.PublicKey, err = crypto.PublicKeyToBytes(publicKey)
if err != nil {
return err
}
key.privateKey = crypto.PrivateKeyToBytes(privateKey)
return nil
}

View File

@@ -1,115 +0,0 @@
package model
import (
"testing"
"github.com/caos/zitadel/internal/user/model"
)
func TestAppendDeactivatedEvent(t *testing.T) {
type args struct {
user *User
}
tests := []struct {
name string
args args
result *User
}{
{
name: "append deactivate event",
args: args{
user: &User{},
},
result: &User{State: int32(model.UserStateInactive)},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.args.user.appendDeactivatedEvent()
if tt.args.user.State != tt.result.State {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
}
})
}
}
func TestAppendReactivatedEvent(t *testing.T) {
type args struct {
user *User
}
tests := []struct {
name string
args args
result *User
}{
{
name: "append reactivate event",
args: args{
user: &User{},
},
result: &User{State: int32(model.UserStateActive)},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.args.user.appendReactivatedEvent()
if tt.args.user.State != tt.result.State {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
}
})
}
}
func TestAppendLockEvent(t *testing.T) {
type args struct {
user *User
}
tests := []struct {
name string
args args
result *User
}{
{
name: "append lock event",
args: args{
user: &User{},
},
result: &User{State: int32(model.UserStateLocked)},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.args.user.appendLockedEvent()
if tt.args.user.State != tt.result.State {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
}
})
}
}
func TestAppendUnlockEvent(t *testing.T) {
type args struct {
user *User
}
tests := []struct {
name string
args args
result *User
}{
{
name: "append unlock event",
args: args{
user: &User{},
},
result: &User{State: int32(model.UserStateActive)},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.args.user.appendUnlockedEvent()
if tt.args.user.State != tt.result.State {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result, tt.args.user)
}
})
}
}

View File

@@ -62,97 +62,6 @@ func GetWebauthn(webauthnTokens []*WebAuthNToken, id string) (int, *WebAuthNToke
return -1, nil
}
func WebAuthNsToModel(u2fs []*WebAuthNToken) []*model.WebAuthNToken {
convertedIDPs := make([]*model.WebAuthNToken, len(u2fs))
for i, m := range u2fs {
convertedIDPs[i] = WebAuthNToModel(m)
}
return convertedIDPs
}
func WebAuthNsFromModel(u2fs []*model.WebAuthNToken) []*WebAuthNToken {
convertedIDPs := make([]*WebAuthNToken, len(u2fs))
for i, m := range u2fs {
convertedIDPs[i] = WebAuthNFromModel(m)
}
return convertedIDPs
}
func WebAuthNFromModel(webAuthN *model.WebAuthNToken) *WebAuthNToken {
return &WebAuthNToken{
ObjectRoot: webAuthN.ObjectRoot,
WebauthNTokenID: webAuthN.WebAuthNTokenID,
Challenge: webAuthN.Challenge,
State: int32(webAuthN.State),
KeyID: webAuthN.KeyID,
PublicKey: webAuthN.PublicKey,
AAGUID: webAuthN.AAGUID,
SignCount: webAuthN.SignCount,
AttestationType: webAuthN.AttestationType,
WebAuthNTokenName: webAuthN.WebAuthNTokenName,
}
}
func WebAuthNToModel(webAuthN *WebAuthNToken) *model.WebAuthNToken {
return &model.WebAuthNToken{
ObjectRoot: webAuthN.ObjectRoot,
WebAuthNTokenID: webAuthN.WebauthNTokenID,
Challenge: webAuthN.Challenge,
State: model.MFAState(webAuthN.State),
KeyID: webAuthN.KeyID,
PublicKey: webAuthN.PublicKey,
AAGUID: webAuthN.AAGUID,
SignCount: webAuthN.SignCount,
AttestationType: webAuthN.AttestationType,
WebAuthNTokenName: webAuthN.WebAuthNTokenName,
}
}
func WebAuthNVerifyFromModel(webAuthN *model.WebAuthNToken, userAgentID string) *WebAuthNVerify {
return &WebAuthNVerify{
WebAuthNTokenID: webAuthN.WebAuthNTokenID,
KeyID: webAuthN.KeyID,
PublicKey: webAuthN.PublicKey,
AAGUID: webAuthN.AAGUID,
SignCount: webAuthN.SignCount,
AttestationType: webAuthN.AttestationType,
WebAuthNTokenName: webAuthN.WebAuthNTokenName,
UserAgentID: userAgentID,
}
}
func WebAuthNLoginsToModel(u2fs []*WebAuthNLogin) []*model.WebAuthNLogin {
convertedIDPs := make([]*model.WebAuthNLogin, len(u2fs))
for i, m := range u2fs {
convertedIDPs[i] = WebAuthNLoginToModel(m)
}
return convertedIDPs
}
func WebAuthNLoginsFromModel(u2fs []*model.WebAuthNLogin) []*WebAuthNLogin {
convertedIDPs := make([]*WebAuthNLogin, len(u2fs))
for i, m := range u2fs {
convertedIDPs[i] = WebAuthNLoginFromModel(m)
}
return convertedIDPs
}
func WebAuthNLoginFromModel(webAuthN *model.WebAuthNLogin) *WebAuthNLogin {
return &WebAuthNLogin{
ObjectRoot: webAuthN.ObjectRoot,
Challenge: webAuthN.Challenge,
AuthRequest: AuthRequestFromModel(webAuthN.AuthRequest),
}
}
func WebAuthNLoginToModel(webAuthN *WebAuthNLogin) *model.WebAuthNLogin {
return &model.WebAuthNLogin{
ObjectRoot: webAuthN.ObjectRoot,
Challenge: webAuthN.Challenge,
AuthRequest: AuthRequestToModel(webAuthN.AuthRequest),
}
}
func (w *WebAuthNVerify) SetData(event *es_models.Event) error {
if err := json.Unmarshal(event.Data, w); err != nil {
logging.Log("EVEN-G342rf").WithError(err).Error("could not unmarshal event data")

View File

@@ -1,7 +1,6 @@
package eventsourcing
package view
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"
@@ -21,9 +20,15 @@ func UserQuery(latestSequence uint64) *es_models.SearchQuery {
LatestSequenceFilter(latestSequence)
}
func UserAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, user *model.User) (*es_models.Aggregate, error) {
if user == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dis83", "Errors.Internal")
func ChangesQuery(userID string, latestSequence, limit uint64, sortAscending bool) *es_models.SearchQuery {
query := es_models.NewSearchQuery().
AggregateTypeFilter(model.UserAggregate)
if !sortAscending {
query.OrderDesc()
}
return aggCreator.NewAggregate(ctx, user.AggregateID, model.UserAggregate, model.UserVersion, user.Sequence)
query.LatestSequenceFilter(latestSequence).
AggregateIDFilter(userID).
SetLimit(limit)
return query
}