From 737e01bfd295cc206624756d5090ddab78b48292 Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Wed, 25 May 2022 14:15:13 +0200 Subject: [PATCH] fix: fix and improve primary keys on projections (#3708) * fix: org_domain projection * fix: projection reset * fix test * improve foreign keys on suffixed tables --- cmd/admin/start/start.go | 2 +- internal/api/grpc/system/server.go | 3 + internal/api/grpc/system/view.go | 2 +- internal/eventstore/handler/crdb/init.go | 38 +++++ internal/query/current_sequence.go | 9 +- internal/query/projection/app.go | 31 +++- internal/query/projection/app_test.go | 51 ++++-- internal/query/projection/idp.go | 28 +++- internal/query/projection/idp_test.go | 72 ++++++--- internal/query/projection/key.go | 34 ++-- internal/query/projection/key_test.go | 6 +- internal/query/projection/org_domain.go | 7 +- internal/query/projection/org_domain_test.go | 15 +- internal/query/projection/sms.go | 14 +- internal/query/projection/sms_test.go | 21 ++- internal/query/projection/smtp.go | 4 +- internal/query/projection/smtp_test.go | 6 +- internal/query/projection/user.go | 57 +++++-- internal/query/projection/user_test.go | 156 ++++++++++++------- proto/zitadel/system.proto | 3 - 20 files changed, 403 insertions(+), 156 deletions(-) diff --git a/cmd/admin/start/start.go b/cmd/admin/start/start.go index ee14552706..56410f608a 100644 --- a/cmd/admin/start/start.go +++ b/cmd/admin/start/start.go @@ -161,7 +161,7 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman if err != nil { return fmt.Errorf("error starting admin repo: %w", err) } - if err := authenticatedAPIs.RegisterServer(ctx, system.CreateServer(commands, queries, adminRepo, config.DefaultInstance)); err != nil { + if err := authenticatedAPIs.RegisterServer(ctx, system.CreateServer(commands, queries, adminRepo, config.Database.Database, config.DefaultInstance)); err != nil { return err } if err := authenticatedAPIs.RegisterServer(ctx, admin.CreateServer(commands, queries, adminRepo, config.ExternalSecure, keys.User)); err != nil { diff --git a/internal/api/grpc/system/server.go b/internal/api/grpc/system/server.go index 3881e83c14..fca6b0a77e 100644 --- a/internal/api/grpc/system/server.go +++ b/internal/api/grpc/system/server.go @@ -21,6 +21,7 @@ var _ system.SystemServiceServer = (*Server)(nil) type Server struct { system.UnimplementedSystemServiceServer + database string command *command.Commands query *query.Queries administrator repository.AdministratorRepository @@ -34,12 +35,14 @@ type Config struct { func CreateServer(command *command.Commands, query *query.Queries, repo repository.Repository, + database string, defaultInstance command.InstanceSetup, ) *Server { return &Server{ command: command, query: query, administrator: repo, + database: database, DefaultInstance: defaultInstance, } } diff --git a/internal/api/grpc/system/view.go b/internal/api/grpc/system/view.go index 9c610e071c..8717420459 100644 --- a/internal/api/grpc/system/view.go +++ b/internal/api/grpc/system/view.go @@ -25,7 +25,7 @@ func (s *Server) ListViews(ctx context.Context, _ *system_pb.ListViewsRequest) ( func (s *Server) ClearView(ctx context.Context, req *system_pb.ClearViewRequest) (*system_pb.ClearViewResponse, error) { var err error - if req.Database != "zitadel" { + if req.Database != s.database { err = s.administrator.ClearView(ctx, req.Database, req.ViewName) } else { err = s.query.ClearCurrentSequence(ctx, req.ViewName) diff --git a/internal/eventstore/handler/crdb/init.go b/internal/eventstore/handler/crdb/init.go index eed08b8bdc..22f42c83bf 100644 --- a/internal/eventstore/handler/crdb/init.go +++ b/internal/eventstore/handler/crdb/init.go @@ -19,6 +19,7 @@ type Table struct { primaryKey PrimaryKey indices []*Index constraints []*Constraint + foreignKeys []*ForeignKey } func NewTable(columns []*Column, key PrimaryKey, opts ...TableOption) *Table { @@ -58,6 +59,12 @@ func WithConstraint(constraint *Constraint) TableOption { } } +func WithForeignKey(key *ForeignKey) TableOption { + return func(table *Table) { + table.foreignKeys = append(table.foreignKeys, key) + } +} + type Column struct { Name string Type ColumnType @@ -158,6 +165,27 @@ type Constraint struct { Columns []string } +func NewForeignKey(name string, columns []string, refColumns []string) *ForeignKey { + i := &ForeignKey{ + Name: name, + Columns: columns, + RefColumns: refColumns, + } + return i +} + +func NewForeignKeyOfPublicKeys(name string) *ForeignKey { + return &ForeignKey{ + Name: name, + } +} + +type ForeignKey struct { + Name string + Columns []string + RefColumns []string +} + //Init implements handler.Init func (h *StatementHandler) Init(ctx context.Context, checks ...*handler.Check) error { for _, check := range checks { @@ -268,6 +296,16 @@ func createTableStatement(table *Table, tableName string, suffix string) string for _, index := range table.indices { stmt += fmt.Sprintf(", INDEX %s (%s)", index.Name, strings.Join(index.Columns, ",")) } + for _, key := range table.foreignKeys { + ref := tableName + if len(key.RefColumns) > 0 { + ref += fmt.Sprintf("(%s)", strings.Join(key.RefColumns, ",")) + } + if len(key.Columns) == 0 { + key.Columns = table.primaryKey + } + stmt += fmt.Sprintf(", CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s ON DELETE CASCADE", key.Name, strings.Join(key.Columns, ","), ref) + } for _, constraint := range table.constraints { stmt += fmt.Sprintf(", CONSTRAINT %s UNIQUE (%s)", constraint.Name, strings.Join(constraint.Columns, ",")) } diff --git a/internal/query/current_sequence.go b/internal/query/current_sequence.go index 2f71e6d45b..198887e1cb 100644 --- a/internal/query/current_sequence.go +++ b/internal/query/current_sequence.go @@ -15,10 +15,9 @@ import ( ) const ( - lockStmtFormat = "INSERT INTO %[1]s" + - " (locker_id, locked_until, projection_name) VALUES ($1, now()+$2::INTERVAL, $3)" + - " ON CONFLICT (projection_name)" + - " DO UPDATE SET locker_id = $1, locked_until = now()+$2::INTERVAL" + lockStmtFormat = "UPDATE %[1]s" + + " set locker_id = $1, locked_until = now()+$2::INTERVAL" + + " WHERE projection_name = $3" lockerIDReset = "reset" ) @@ -136,7 +135,7 @@ func (q *Queries) checkAndLock(ctx context.Context, projectionName string) error if err != nil || rows == 0 { return errors.ThrowInternal(err, "QUERY-Bh3ws", "Errors.RemoveFailed") } - time.Sleep(7 * time.Second) //more than twice the default lock duration (10s) + time.Sleep(7 * time.Second) //more than half the default lock duration (10s) return nil } diff --git a/internal/query/projection/app.go b/internal/query/projection/app.go index 404aaa8b1b..60375af1ad 100644 --- a/internal/query/projection/app.go +++ b/internal/query/projection/app.go @@ -30,12 +30,14 @@ const ( appAPITableSuffix = "api_configs" AppAPIConfigColumnAppID = "app_id" + AppAPIConfigColumnInstanceID = "instance_id" AppAPIConfigColumnClientID = "client_id" AppAPIConfigColumnClientSecret = "client_secret" AppAPIConfigColumnAuthMethod = "auth_method" appOIDCTableSuffix = "oidc_configs" AppOIDCConfigColumnAppID = "app_id" + AppOIDCConfigColumnInstanceID = "instance_id" AppOIDCConfigColumnVersion = "version" AppOIDCConfigColumnClientID = "client_id" AppOIDCConfigColumnClientSecret = "client_secret" @@ -74,22 +76,25 @@ func NewAppProjection(ctx context.Context, config crdb.StatementHandlerConfig) * crdb.NewColumn(AppColumnState, crdb.ColumnTypeEnum), crdb.NewColumn(AppColumnSequence, crdb.ColumnTypeInt64), }, - crdb.NewPrimaryKey(AppColumnInstanceID, ActionIDCol), + crdb.NewPrimaryKey(AppColumnID, AppColumnInstanceID), crdb.WithIndex(crdb.NewIndex("project_id_idx", []string{AppColumnProjectID})), crdb.WithConstraint(crdb.NewConstraint("id_unique", []string{AppColumnID})), ), crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(AppAPIConfigColumnAppID, crdb.ColumnTypeText, crdb.DeleteCascade(AppColumnID)), + crdb.NewColumn(AppAPIConfigColumnAppID, crdb.ColumnTypeText), + crdb.NewColumn(AppAPIConfigColumnInstanceID, crdb.ColumnTypeText), crdb.NewColumn(AppAPIConfigColumnClientID, crdb.ColumnTypeText), crdb.NewColumn(AppAPIConfigColumnClientSecret, crdb.ColumnTypeJSONB, crdb.Nullable()), crdb.NewColumn(AppAPIConfigColumnAuthMethod, crdb.ColumnTypeEnum), }, crdb.NewPrimaryKey(AppAPIConfigColumnAppID), appAPITableSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_api_ref_apps")), crdb.WithIndex(crdb.NewIndex("client_id_idx", []string{AppAPIConfigColumnClientID})), ), crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(AppOIDCConfigColumnAppID, crdb.ColumnTypeText, crdb.DeleteCascade(AppColumnID)), + crdb.NewColumn(AppOIDCConfigColumnAppID, crdb.ColumnTypeText), + crdb.NewColumn(AppOIDCConfigColumnInstanceID, crdb.ColumnTypeText), crdb.NewColumn(AppOIDCConfigColumnVersion, crdb.ColumnTypeText), crdb.NewColumn(AppOIDCConfigColumnClientID, crdb.ColumnTypeText), crdb.NewColumn(AppOIDCConfigColumnClientSecret, crdb.ColumnTypeJSONB, crdb.Nullable()), @@ -107,8 +112,9 @@ func NewAppProjection(ctx context.Context, config crdb.StatementHandlerConfig) * crdb.NewColumn(AppOIDCConfigColumnClockSkew, crdb.ColumnTypeInt64, crdb.Default(0)), crdb.NewColumn(AppOIDCConfigColumnAdditionalOrigins, crdb.ColumnTypeTextArray, crdb.Nullable()), }, - crdb.NewPrimaryKey(AppOIDCConfigColumnAppID), + crdb.NewPrimaryKey(AppOIDCConfigColumnAppID, AppOIDCConfigColumnInstanceID), appOIDCTableSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_oidc_ref_apps")), crdb.WithIndex(crdb.NewIndex("client_id_idx", []string{AppOIDCConfigColumnClientID})), ), ) @@ -212,6 +218,7 @@ func (p *AppProjection) reduceAppChanged(event eventstore.Event) (*handler.State }, []handler.Condition{ handler.NewCond(AppColumnID, e.AppID), + handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } @@ -230,6 +237,7 @@ func (p *AppProjection) reduceAppDeactivated(event eventstore.Event) (*handler.S }, []handler.Condition{ handler.NewCond(AppColumnID, e.AppID), + handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } @@ -248,6 +256,7 @@ func (p *AppProjection) reduceAppReactivated(event eventstore.Event) (*handler.S }, []handler.Condition{ handler.NewCond(AppColumnID, e.AppID), + handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } @@ -261,6 +270,7 @@ func (p *AppProjection) reduceAppRemoved(event eventstore.Event) (*handler.State e, []handler.Condition{ handler.NewCond(AppColumnID, e.AppID), + handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } @@ -274,6 +284,7 @@ func (p *AppProjection) reduceProjectRemoved(event eventstore.Event) (*handler.S e, []handler.Condition{ handler.NewCond(AppColumnProjectID, e.Aggregate().ID), + handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } @@ -288,6 +299,7 @@ func (p *AppProjection) reduceAPIConfigAdded(event eventstore.Event) (*handler.S crdb.AddCreateStatement( []handler.Column{ handler.NewCol(AppAPIConfigColumnAppID, e.AppID), + handler.NewCol(AppAPIConfigColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(AppAPIConfigColumnClientID, e.ClientID), handler.NewCol(AppAPIConfigColumnClientSecret, e.ClientSecret), handler.NewCol(AppAPIConfigColumnAuthMethod, e.AuthMethodType), @@ -301,6 +313,7 @@ func (p *AppProjection) reduceAPIConfigAdded(event eventstore.Event) (*handler.S }, []handler.Condition{ handler.NewCond(AppColumnID, e.AppID), + handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID), }, ), ), nil @@ -327,6 +340,7 @@ func (p *AppProjection) reduceAPIConfigChanged(event eventstore.Event) (*handler cols, []handler.Condition{ handler.NewCond(AppAPIConfigColumnAppID, e.AppID), + handler.NewCond(AppAPIConfigColumnInstanceID, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(appAPITableSuffix), ), @@ -337,6 +351,7 @@ func (p *AppProjection) reduceAPIConfigChanged(event eventstore.Event) (*handler }, []handler.Condition{ handler.NewCond(AppColumnID, e.AppID), + handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID), }, ), ), nil @@ -355,6 +370,7 @@ func (p *AppProjection) reduceAPIConfigSecretChanged(event eventstore.Event) (*h }, []handler.Condition{ handler.NewCond(AppAPIConfigColumnAppID, e.AppID), + handler.NewCond(AppAPIConfigColumnInstanceID, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(appAPITableSuffix), ), @@ -365,6 +381,7 @@ func (p *AppProjection) reduceAPIConfigSecretChanged(event eventstore.Event) (*h }, []handler.Condition{ handler.NewCond(AppColumnID, e.AppID), + handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID), }, ), ), nil @@ -380,6 +397,7 @@ func (p *AppProjection) reduceOIDCConfigAdded(event eventstore.Event) (*handler. crdb.AddCreateStatement( []handler.Column{ handler.NewCol(AppOIDCConfigColumnAppID, e.AppID), + handler.NewCol(AppOIDCConfigColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(AppOIDCConfigColumnVersion, e.Version), handler.NewCol(AppOIDCConfigColumnClientID, e.ClientID), handler.NewCol(AppOIDCConfigColumnClientSecret, e.ClientSecret), @@ -406,6 +424,7 @@ func (p *AppProjection) reduceOIDCConfigAdded(event eventstore.Event) (*handler. }, []handler.Condition{ handler.NewCond(AppColumnID, e.AppID), + handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID), }, ), ), nil @@ -471,6 +490,7 @@ func (p *AppProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle cols, []handler.Condition{ handler.NewCond(AppOIDCConfigColumnAppID, e.AppID), + handler.NewCond(AppOIDCConfigColumnInstanceID, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(appOIDCTableSuffix), ), @@ -481,6 +501,7 @@ func (p *AppProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle }, []handler.Condition{ handler.NewCond(AppColumnID, e.AppID), + handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID), }, ), ), nil @@ -499,6 +520,7 @@ func (p *AppProjection) reduceOIDCConfigSecretChanged(event eventstore.Event) (* }, []handler.Condition{ handler.NewCond(AppOIDCConfigColumnAppID, e.AppID), + handler.NewCond(AppOIDCConfigColumnInstanceID, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(appOIDCTableSuffix), ), @@ -509,6 +531,7 @@ func (p *AppProjection) reduceOIDCConfigSecretChanged(event eventstore.Event) (* }, []handler.Condition{ handler.NewCond(AppColumnID, e.AppID), + handler.NewCond(AppColumnInstanceID, e.Aggregate().InstanceID), }, ), ), nil diff --git a/internal/query/projection/app_test.go b/internal/query/projection/app_test.go index d0f99d5aca..63f80c013b 100644 --- a/internal/query/projection/app_test.go +++ b/internal/query/projection/app_test.go @@ -83,12 +83,13 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.apps SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ "my-app", anyArg{}, uint64(15), "app-id", + "instance-id", }, }, }, @@ -115,12 +116,13 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.apps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ domain.AppStateInactive, anyArg{}, uint64(15), "app-id", + "instance-id", }, }, }, @@ -147,12 +149,13 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.apps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ domain.AppStateActive, anyArg{}, uint64(15), "app-id", + "instance-id", }, }, }, @@ -179,9 +182,10 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.apps WHERE (id = $1)", + expectedStmt: "DELETE FROM projections.apps WHERE (id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "app-id", + "instance-id", }, }, }, @@ -206,9 +210,10 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.apps WHERE (project_id = $1)", + expectedStmt: "DELETE FROM projections.apps WHERE (project_id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", + "instance-id", }, }, }, @@ -238,20 +243,22 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.apps_api_configs (app_id, client_id, client_secret, auth_method) VALUES ($1, $2, $3, $4)", + expectedStmt: "INSERT INTO projections.apps_api_configs (app_id, instance_id, client_id, client_secret, auth_method) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "app-id", + "instance-id", "client-id", anyArg{}, domain.APIAuthMethodTypePrivateKeyJWT, }, }, { - expectedStmt: "UPDATE projections.apps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.apps SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "app-id", + "instance-id", }, }, }, @@ -281,19 +288,21 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps_api_configs SET (client_secret, auth_method) = ($1, $2) WHERE (app_id = $3)", + expectedStmt: "UPDATE projections.apps_api_configs SET (client_secret, auth_method) = ($1, $2) WHERE (app_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, domain.APIAuthMethodTypePrivateKeyJWT, "app-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.apps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.apps SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "app-id", + "instance-id", }, }, }, @@ -343,18 +352,20 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps_api_configs SET (client_secret) = ($1) WHERE (app_id = $2)", + expectedStmt: "UPDATE projections.apps_api_configs SET (client_secret) = ($1) WHERE (app_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ anyArg{}, "app-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.apps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.apps SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "app-id", + "instance-id", }, }, }, @@ -397,9 +408,10 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.apps_oidc_configs (app_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)", + expectedStmt: "INSERT INTO projections.apps_oidc_configs (app_id, instance_id, version, client_id, client_secret, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)", expectedArgs: []interface{}{ "app-id", + "instance-id", domain.OIDCVersionV1, "client-id", anyArg{}, @@ -419,11 +431,12 @@ func TestAppProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.apps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.apps SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "app-id", + "instance-id", }, }, }, @@ -464,7 +477,7 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) WHERE (app_id = $15)", + expectedStmt: "UPDATE projections.apps_oidc_configs SET (version, redirect_uris, response_types, grant_types, application_type, auth_method_type, post_logout_redirect_uris, is_dev_mode, access_token_type, access_token_role_assertion, id_token_role_assertion, id_token_userinfo_assertion, clock_skew, additional_origins) = ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) WHERE (app_id = $15) AND (instance_id = $16)", expectedArgs: []interface{}{ domain.OIDCVersionV1, pq.StringArray{"redirect.one.ch", "redirect.two.ch"}, @@ -481,14 +494,16 @@ func TestAppProjection_reduces(t *testing.T) { 1 * time.Microsecond, pq.StringArray{"origin.one.ch", "origin.two.ch"}, "app-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.apps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.apps SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "app-id", + "instance-id", }, }, }, @@ -538,18 +553,20 @@ func TestAppProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.apps_oidc_configs SET (client_secret) = ($1) WHERE (app_id = $2)", + expectedStmt: "UPDATE projections.apps_oidc_configs SET (client_secret) = ($1) WHERE (app_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ anyArg{}, "app-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.apps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.apps SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "app-id", + "instance-id", }, }, }, diff --git a/internal/query/projection/idp.go b/internal/query/projection/idp.go index 1c8c3fd18f..27a64c6f67 100644 --- a/internal/query/projection/idp.go +++ b/internal/query/projection/idp.go @@ -37,6 +37,7 @@ const ( IDPTypeCol = "type" OIDCConfigIDPIDCol = "idp_id" + OIDCConfigInstanceIDCol = "instance_id" OIDCConfigClientIDCol = "client_id" OIDCConfigClientSecretCol = "client_secret" OIDCConfigIssuerCol = "issuer" @@ -47,6 +48,7 @@ const ( OIDCConfigTokenEndpointCol = "token_endpoint" JWTConfigIDPIDCol = "idp_id" + JWTConfigInstanceIDCol = "instance_id" JWTConfigIssuerCol = "issuer" JWTConfigKeysEndpointCol = "keys_endpoint" JWTConfigHeaderNameCol = "header_name" @@ -76,12 +78,13 @@ func NewIDPProjection(ctx context.Context, config crdb.StatementHandlerConfig) * crdb.NewColumn(IDPAutoRegisterCol, crdb.ColumnTypeBool, crdb.Default(false)), crdb.NewColumn(IDPTypeCol, crdb.ColumnTypeEnum), }, - crdb.NewPrimaryKey(IDPInstanceIDCol, IDPIDCol), + crdb.NewPrimaryKey(IDPIDCol, IDPInstanceIDCol), crdb.WithIndex(crdb.NewIndex("ro_idx", []string{IDPResourceOwnerCol})), crdb.WithConstraint(crdb.NewConstraint("id_unique", []string{IDPIDCol})), ), crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(OIDCConfigIDPIDCol, crdb.ColumnTypeText, crdb.DeleteCascade(IDPIDCol)), + crdb.NewColumn(OIDCConfigIDPIDCol, crdb.ColumnTypeText), + crdb.NewColumn(OIDCConfigInstanceIDCol, crdb.ColumnTypeText), crdb.NewColumn(OIDCConfigClientIDCol, crdb.ColumnTypeText, crdb.Nullable()), crdb.NewColumn(OIDCConfigClientSecretCol, crdb.ColumnTypeJSONB, crdb.Nullable()), crdb.NewColumn(OIDCConfigIssuerCol, crdb.ColumnTypeText, crdb.Nullable()), @@ -93,9 +96,11 @@ func NewIDPProjection(ctx context.Context, config crdb.StatementHandlerConfig) * }, crdb.NewPrimaryKey(OIDCConfigIDPIDCol), IDPOIDCSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_oidc_ref_idp")), ), crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(JWTConfigIDPIDCol, crdb.ColumnTypeText, crdb.DeleteCascade(IDPIDCol)), + crdb.NewColumn(JWTConfigIDPIDCol, crdb.ColumnTypeText), + crdb.NewColumn(JWTConfigInstanceIDCol, crdb.ColumnTypeText), crdb.NewColumn(JWTConfigIssuerCol, crdb.ColumnTypeText, crdb.Nullable()), crdb.NewColumn(JWTConfigKeysEndpointCol, crdb.ColumnTypeText, crdb.Nullable()), crdb.NewColumn(JWTConfigHeaderNameCol, crdb.ColumnTypeText, crdb.Nullable()), @@ -103,6 +108,7 @@ func NewIDPProjection(ctx context.Context, config crdb.StatementHandlerConfig) * }, crdb.NewPrimaryKey(JWTConfigIDPIDCol), IDPJWTSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_jwt_ref_idp")), ), ) p.StatementHandler = crdb.NewStatementHandler(ctx, config) @@ -263,6 +269,7 @@ func (p *IDPProjection) reduceIDPChanged(event eventstore.Event) (*handler.State cols, []handler.Condition{ handler.NewCond(IDPIDCol, idpEvent.ConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), nil } @@ -287,6 +294,7 @@ func (p *IDPProjection) reduceIDPDeactivated(event eventstore.Event) (*handler.S }, []handler.Condition{ handler.NewCond(IDPIDCol, idpEvent.ConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), nil } @@ -311,6 +319,7 @@ func (p *IDPProjection) reduceIDPReactivated(event eventstore.Event) (*handler.S }, []handler.Condition{ handler.NewCond(IDPIDCol, idpEvent.ConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), nil } @@ -330,6 +339,7 @@ func (p *IDPProjection) reduceIDPRemoved(event eventstore.Event) (*handler.State &idpEvent, []handler.Condition{ handler.NewCond(IDPIDCol, idpEvent.ConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), nil } @@ -354,11 +364,13 @@ func (p *IDPProjection) reduceOIDCConfigAdded(event eventstore.Event) (*handler. }, []handler.Condition{ handler.NewCond(IDPIDCol, idpEvent.IDPConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), crdb.AddCreateStatement( []handler.Column{ handler.NewCol(OIDCConfigIDPIDCol, idpEvent.IDPConfigID), + handler.NewCol(OIDCConfigInstanceIDCol, idpEvent.Aggregate().InstanceID), handler.NewCol(OIDCConfigClientIDCol, idpEvent.ClientID), handler.NewCol(OIDCConfigClientSecretCol, idpEvent.ClientSecret), handler.NewCol(OIDCConfigIssuerCol, idpEvent.Issuer), @@ -423,12 +435,14 @@ func (p *IDPProjection) reduceOIDCConfigChanged(event eventstore.Event) (*handle }, []handler.Condition{ handler.NewCond(IDPIDCol, idpEvent.IDPConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), crdb.AddUpdateStatement( cols, []handler.Condition{ handler.NewCond(OIDCConfigIDPIDCol, idpEvent.IDPConfigID), + handler.NewCond(OIDCConfigInstanceIDCol, idpEvent.Aggregate().InstanceID), }, crdb.WithTableSuffix(IDPOIDCSuffix), ), @@ -455,12 +469,14 @@ func (p *IDPProjection) reduceJWTConfigAdded(event eventstore.Event) (*handler.S }, []handler.Condition{ handler.NewCond(IDPIDCol, idpEvent.IDPConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), crdb.AddCreateStatement( []handler.Column{ - handler.NewCol(OIDCConfigIDPIDCol, idpEvent.IDPConfigID), + handler.NewCol(JWTConfigIDPIDCol, idpEvent.IDPConfigID), + handler.NewCol(JWTConfigInstanceIDCol, idpEvent.Aggregate().InstanceID), handler.NewCol(JWTConfigEndpointCol, idpEvent.JWTEndpoint), handler.NewCol(JWTConfigIssuerCol, idpEvent.Issuer), handler.NewCol(JWTConfigKeysEndpointCol, idpEvent.KeysEndpoint), @@ -509,12 +525,14 @@ func (p *IDPProjection) reduceJWTConfigChanged(event eventstore.Event) (*handler }, []handler.Condition{ handler.NewCond(IDPIDCol, idpEvent.IDPConfigID), + handler.NewCond(IDPInstanceIDCol, idpEvent.Aggregate().InstanceID), }, ), crdb.AddUpdateStatement( cols, []handler.Condition{ - handler.NewCond(OIDCConfigIDPIDCol, idpEvent.IDPConfigID), + handler.NewCond(JWTConfigIDPIDCol, idpEvent.IDPConfigID), + handler.NewCond(JWTConfigInstanceIDCol, idpEvent.Aggregate().InstanceID), }, crdb.WithTableSuffix(IDPJWTSuffix), ), diff --git a/internal/query/projection/idp_test.go b/internal/query/projection/idp_test.go index 70dedd907e..79bddb523f 100644 --- a/internal/query/projection/idp_test.go +++ b/internal/query/projection/idp_test.go @@ -90,7 +90,7 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (name, styling_type, auto_register, change_date, sequence) = ($1, $2, $3, $4, $5) WHERE (id = $6)", + expectedStmt: "UPDATE projections.idps SET (name, styling_type, auto_register, change_date, sequence) = ($1, $2, $3, $4, $5) WHERE (id = $6) AND (instance_id = $7)", expectedArgs: []interface{}{ "custom-zitadel-instance", domain.IDPConfigStylingTypeGoogle, @@ -98,6 +98,7 @@ func TestIDPProjection_reduces(t *testing.T) { anyArg{}, uint64(15), "idp-config-id", + "instance-id", }, }, }, @@ -124,12 +125,13 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.idps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ domain.IDPConfigStateInactive, anyArg{}, uint64(15), "idp-config-id", + "instance-id", }, }, }, @@ -156,12 +158,13 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.idps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ domain.IDPConfigStateActive, anyArg{}, uint64(15), "idp-config-id", + "instance-id", }, }, }, @@ -188,9 +191,10 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idps WHERE (id = $1)", + expectedStmt: "DELETE FROM projections.idps WHERE (id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "idp-config-id", + "instance-id", }, }, }, @@ -229,18 +233,20 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (change_date, sequence, type) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.idps SET (change_date, sequence, type) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, uint64(15), domain.IDPConfigTypeOIDC, "idp-config-id", + "instance-id", }, }, { - expectedStmt: "INSERT INTO projections.idps_oidc_config (idp_id, client_id, client_secret, issuer, scopes, display_name_mapping, username_mapping, authorization_endpoint, token_endpoint) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.idps_oidc_config (idp_id, instance_id, client_id, client_secret, issuer, scopes, display_name_mapping, username_mapping, authorization_endpoint, token_endpoint) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", expectedArgs: []interface{}{ "idp-config-id", + "instance-id", "client-id", anyArg{}, "issuer", @@ -287,15 +293,16 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "idp-config-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.idps_oidc_config SET (client_id, client_secret, issuer, authorization_endpoint, token_endpoint, scopes, display_name_mapping, username_mapping) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (idp_id = $9)", + expectedStmt: "UPDATE projections.idps_oidc_config SET (client_id, client_secret, issuer, authorization_endpoint, token_endpoint, scopes, display_name_mapping, username_mapping) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (idp_id = $9) AND (instance_id = $10)", expectedArgs: []interface{}{ "client-id", anyArg{}, @@ -306,6 +313,7 @@ func TestIDPProjection_reduces(t *testing.T) { domain.OIDCMappingFieldUnspecified, domain.OIDCMappingFieldPreferredLoginName, "idp-config-id", + "instance-id", }, }, }, @@ -356,18 +364,20 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (change_date, sequence, type) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.idps SET (change_date, sequence, type) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, uint64(15), domain.IDPConfigTypeJWT, "idp-config-id", + "instance-id", }, }, { - expectedStmt: "INSERT INTO projections.idps_jwt_config (idp_id, endpoint, issuer, keys_endpoint, header_name) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.idps_jwt_config (idp_id, instance_id, endpoint, issuer, keys_endpoint, header_name) VALUES ($1, $2, $3, $4, $5, $6)", expectedArgs: []interface{}{ "idp-config-id", + "instance-id", "https://api.zitadel.ch/jwt", "issuer", "https://api.zitadel.ch/keys", @@ -402,21 +412,23 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "idp-config-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.idps_jwt_config SET (endpoint, issuer, keys_endpoint, header_name) = ($1, $2, $3, $4) WHERE (idp_id = $5)", + expectedStmt: "UPDATE projections.idps_jwt_config SET (endpoint, issuer, keys_endpoint, header_name) = ($1, $2, $3, $4) WHERE (idp_id = $5) AND (instance_id = $6)", expectedArgs: []interface{}{ "https://api.zitadel.ch/jwt", "issuer", "https://api.zitadel.ch/keys", "hodor", "idp-config-id", + "instance-id", }, }, }, @@ -509,7 +521,7 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (name, styling_type, auto_register, change_date, sequence) = ($1, $2, $3, $4, $5) WHERE (id = $6)", + expectedStmt: "UPDATE projections.idps SET (name, styling_type, auto_register, change_date, sequence) = ($1, $2, $3, $4, $5) WHERE (id = $6) AND (instance_id = $7)", expectedArgs: []interface{}{ "custom-zitadel-instance", domain.IDPConfigStylingTypeGoogle, @@ -517,6 +529,7 @@ func TestIDPProjection_reduces(t *testing.T) { anyArg{}, uint64(15), "idp-config-id", + "instance-id", }, }, }, @@ -543,12 +556,13 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.idps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ domain.IDPConfigStateInactive, anyArg{}, uint64(15), "idp-config-id", + "instance-id", }, }, }, @@ -575,12 +589,13 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.idps SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ domain.IDPConfigStateActive, anyArg{}, uint64(15), "idp-config-id", + "instance-id", }, }, }, @@ -607,9 +622,10 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.idps WHERE (id = $1)", + expectedStmt: "DELETE FROM projections.idps WHERE (id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "idp-config-id", + "instance-id", }, }, }, @@ -648,18 +664,20 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (change_date, sequence, type) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.idps SET (change_date, sequence, type) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, uint64(15), domain.IDPConfigTypeOIDC, "idp-config-id", + "instance-id", }, }, { - expectedStmt: "INSERT INTO projections.idps_oidc_config (idp_id, client_id, client_secret, issuer, scopes, display_name_mapping, username_mapping, authorization_endpoint, token_endpoint) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.idps_oidc_config (idp_id, instance_id, client_id, client_secret, issuer, scopes, display_name_mapping, username_mapping, authorization_endpoint, token_endpoint) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", expectedArgs: []interface{}{ "idp-config-id", + "instance-id", "client-id", anyArg{}, "issuer", @@ -706,15 +724,16 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "idp-config-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.idps_oidc_config SET (client_id, client_secret, issuer, authorization_endpoint, token_endpoint, scopes, display_name_mapping, username_mapping) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (idp_id = $9)", + expectedStmt: "UPDATE projections.idps_oidc_config SET (client_id, client_secret, issuer, authorization_endpoint, token_endpoint, scopes, display_name_mapping, username_mapping) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (idp_id = $9) AND (instance_id = $10)", expectedArgs: []interface{}{ "client-id", anyArg{}, @@ -725,6 +744,7 @@ func TestIDPProjection_reduces(t *testing.T) { domain.OIDCMappingFieldUnspecified, domain.OIDCMappingFieldPreferredLoginName, "idp-config-id", + "instance-id", }, }, }, @@ -775,18 +795,20 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (change_date, sequence, type) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.idps SET (change_date, sequence, type) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, uint64(15), domain.IDPConfigTypeJWT, "idp-config-id", + "instance-id", }, }, { - expectedStmt: "INSERT INTO projections.idps_jwt_config (idp_id, endpoint, issuer, keys_endpoint, header_name) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.idps_jwt_config (idp_id, instance_id, endpoint, issuer, keys_endpoint, header_name) VALUES ($1, $2, $3, $4, $5, $6)", expectedArgs: []interface{}{ "idp-config-id", + "instance-id", "https://api.zitadel.ch/jwt", "issuer", "https://api.zitadel.ch/keys", @@ -821,21 +843,23 @@ func TestIDPProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.idps SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "idp-config-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.idps_jwt_config SET (endpoint, issuer, keys_endpoint, header_name) = ($1, $2, $3, $4) WHERE (idp_id = $5)", + expectedStmt: "UPDATE projections.idps_jwt_config SET (endpoint, issuer, keys_endpoint, header_name) = ($1, $2, $3, $4) WHERE (idp_id = $5) AND (instance_id = $6)", expectedArgs: []interface{}{ "https://api.zitadel.ch/jwt", "issuer", "https://api.zitadel.ch/keys", "hodor", "idp-config-id", + "instance-id", }, }, }, diff --git a/internal/query/projection/key.go b/internal/query/projection/key.go index 4920030c0d..3adacdd212 100644 --- a/internal/query/projection/key.go +++ b/internal/query/projection/key.go @@ -26,15 +26,17 @@ const ( KeyColumnAlgorithm = "algorithm" KeyColumnUse = "use" - privateKeyTableSuffix = "private" - KeyPrivateColumnID = "id" - KeyPrivateColumnExpiry = "expiry" - KeyPrivateColumnKey = "key" + privateKeyTableSuffix = "private" + KeyPrivateColumnID = "id" + KeyPrivateColumnInstanceID = "instance_id" + KeyPrivateColumnExpiry = "expiry" + KeyPrivateColumnKey = "key" - publicKeyTableSuffix = "public" - KeyPublicColumnID = "id" - KeyPublicColumnExpiry = "expiry" - KeyPublicColumnKey = "key" + publicKeyTableSuffix = "public" + KeyPublicColumnID = "id" + KeyPublicColumnInstanceID = "instance_id" + KeyPublicColumnExpiry = "expiry" + KeyPublicColumnKey = "key" ) type KeyProjection struct { @@ -57,24 +59,28 @@ func NewKeyProjection(ctx context.Context, config crdb.StatementHandlerConfig, k crdb.NewColumn(KeyColumnAlgorithm, crdb.ColumnTypeText, crdb.Default("")), crdb.NewColumn(KeyColumnUse, crdb.ColumnTypeEnum, crdb.Default(0)), }, - crdb.NewPrimaryKey(KeyColumnInstanceID, KeyColumnID), + crdb.NewPrimaryKey(KeyColumnID, KeyColumnInstanceID), crdb.WithConstraint(crdb.NewConstraint("id_unique", []string{KeyColumnID})), ), crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(KeyPrivateColumnID, crdb.ColumnTypeText, crdb.DeleteCascade(KeyColumnID)), + crdb.NewColumn(KeyPrivateColumnID, crdb.ColumnTypeText), + crdb.NewColumn(KeyPrivateColumnInstanceID, crdb.ColumnTypeText), crdb.NewColumn(KeyPrivateColumnExpiry, crdb.ColumnTypeTimestamp), crdb.NewColumn(KeyPrivateColumnKey, crdb.ColumnTypeJSONB), }, - crdb.NewPrimaryKey(KeyPrivateColumnID), + crdb.NewPrimaryKey(KeyPrivateColumnID, KeyPrivateColumnInstanceID), privateKeyTableSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_private_ref_keys")), ), crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(KeyPublicColumnID, crdb.ColumnTypeText, crdb.DeleteCascade(KeyColumnID)), + crdb.NewColumn(KeyPublicColumnID, crdb.ColumnTypeText), + crdb.NewColumn(KeyPublicColumnInstanceID, crdb.ColumnTypeText), crdb.NewColumn(KeyPublicColumnExpiry, crdb.ColumnTypeTimestamp), crdb.NewColumn(KeyPublicColumnKey, crdb.ColumnTypeBytes), }, - crdb.NewPrimaryKey(KeyPublicColumnID), + crdb.NewPrimaryKey(KeyPublicColumnID, KeyPublicColumnInstanceID), publicKeyTableSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_public_ref_keys")), ), ) p.StatementHandler = crdb.NewStatementHandler(ctx, config) @@ -123,6 +129,7 @@ func (p *KeyProjection) reduceKeyPairAdded(event eventstore.Event) (*handler.Sta creates = append(creates, crdb.AddCreateStatement( []handler.Column{ handler.NewCol(KeyPrivateColumnID, e.Aggregate().ID), + handler.NewCol(KeyPrivateColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(KeyPrivateColumnExpiry, e.PrivateKey.Expiry), handler.NewCol(KeyPrivateColumnKey, e.PrivateKey.Key), }, @@ -137,6 +144,7 @@ func (p *KeyProjection) reduceKeyPairAdded(event eventstore.Event) (*handler.Sta creates = append(creates, crdb.AddCreateStatement( []handler.Column{ handler.NewCol(KeyPublicColumnID, e.Aggregate().ID), + handler.NewCol(KeyPublicColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(KeyPublicColumnExpiry, e.PublicKey.Expiry), handler.NewCol(KeyPublicColumnKey, publicKey), }, diff --git a/internal/query/projection/key_test.go b/internal/query/projection/key_test.go index 3b028e1662..084017a5b9 100644 --- a/internal/query/projection/key_test.go +++ b/internal/query/projection/key_test.go @@ -56,9 +56,10 @@ func TestKeyProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.keys_private (id, expiry, key) VALUES ($1, $2, $3)", + expectedStmt: "INSERT INTO projections.keys_private (id, instance_id, expiry, key) VALUES ($1, $2, $3, $4)", expectedArgs: []interface{}{ "agg-id", + "instance-id", anyArg{}, &crypto.CryptoValue{ CryptoType: crypto.TypeEncryption, @@ -69,9 +70,10 @@ func TestKeyProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.keys_public (id, expiry, key) VALUES ($1, $2, $3)", + expectedStmt: "INSERT INTO projections.keys_public (id, instance_id, expiry, key) VALUES ($1, $2, $3, $4)", expectedArgs: []interface{}{ "agg-id", + "instance-id", anyArg{}, []byte("publicKey"), }, diff --git a/internal/query/projection/org_domain.go b/internal/query/projection/org_domain.go index f47bcb2a2d..737ebc77e9 100644 --- a/internal/query/projection/org_domain.go +++ b/internal/query/projection/org_domain.go @@ -45,7 +45,7 @@ func NewOrgDomainProjection(ctx context.Context, config crdb.StatementHandlerCon crdb.NewColumn(OrgDomainIsPrimaryCol, crdb.ColumnTypeBool), crdb.NewColumn(OrgDomainValidationTypeCol, crdb.ColumnTypeEnum), }, - crdb.NewPrimaryKey(OrgDomainInstanceIDCol, OrgDomainOrgIDCol), + crdb.NewPrimaryKey(OrgDomainOrgIDCol, OrgDomainDomainCol, OrgDomainInstanceIDCol), ), ) p.StatementHandler = crdb.NewStatementHandler(ctx, config) @@ -118,6 +118,7 @@ func (p *OrgDomainProjection) reduceDomainVerificationAdded(event eventstore.Eve []handler.Condition{ handler.NewCond(OrgDomainDomainCol, e.Domain), handler.NewCond(OrgDomainOrgIDCol, e.Aggregate().ID), + handler.NewCond(OrgDomainInstanceIDCol, e.Aggregate().InstanceID), }, ), nil } @@ -137,6 +138,7 @@ func (p *OrgDomainProjection) reduceDomainVerified(event eventstore.Event) (*han []handler.Condition{ handler.NewCond(OrgDomainDomainCol, e.Domain), handler.NewCond(OrgDomainOrgIDCol, e.Aggregate().ID), + handler.NewCond(OrgDomainInstanceIDCol, e.Aggregate().InstanceID), }, ), nil } @@ -157,6 +159,7 @@ func (p *OrgDomainProjection) reducePrimaryDomainSet(event eventstore.Event) (*h []handler.Condition{ handler.NewCond(OrgDomainOrgIDCol, e.Aggregate().ID), handler.NewCond(OrgDomainIsPrimaryCol, true), + handler.NewCond(OrgDomainInstanceIDCol, e.Aggregate().InstanceID), }, ), crdb.AddUpdateStatement( @@ -168,6 +171,7 @@ func (p *OrgDomainProjection) reducePrimaryDomainSet(event eventstore.Event) (*h []handler.Condition{ handler.NewCond(OrgDomainDomainCol, e.Domain), handler.NewCond(OrgDomainOrgIDCol, e.Aggregate().ID), + handler.NewCond(OrgDomainInstanceIDCol, e.Aggregate().InstanceID), }, ), ), nil @@ -183,6 +187,7 @@ func (p *OrgDomainProjection) reduceDomainRemoved(event eventstore.Event) (*hand []handler.Condition{ handler.NewCond(OrgDomainDomainCol, e.Domain), handler.NewCond(OrgDomainOrgIDCol, e.Aggregate().ID), + handler.NewCond(OrgDomainInstanceIDCol, e.Aggregate().InstanceID), }, ), nil } diff --git a/internal/query/projection/org_domain_test.go b/internal/query/projection/org_domain_test.go index 40b2f0fba8..bbdcb02d2b 100644 --- a/internal/query/projection/org_domain_test.go +++ b/internal/query/projection/org_domain_test.go @@ -74,13 +74,14 @@ func TestOrgDomainProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.org_domains SET (change_date, sequence, validation_type) = ($1, $2, $3) WHERE (domain = $4) AND (org_id = $5)", + expectedStmt: "UPDATE projections.org_domains SET (change_date, sequence, validation_type) = ($1, $2, $3) WHERE (domain = $4) AND (org_id = $5) AND (instance_id = $6)", expectedArgs: []interface{}{ anyArg{}, uint64(15), domain.OrgDomainValidationTypeDNS, "domain.new", "agg-id", + "instance-id", }, }, }, @@ -105,13 +106,14 @@ func TestOrgDomainProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.org_domains SET (change_date, sequence, is_verified) = ($1, $2, $3) WHERE (domain = $4) AND (org_id = $5)", + expectedStmt: "UPDATE projections.org_domains SET (change_date, sequence, is_verified) = ($1, $2, $3) WHERE (domain = $4) AND (org_id = $5) AND (instance_id = $6)", expectedArgs: []interface{}{ anyArg{}, uint64(15), true, "domain.new", "agg-id", + "instance-id", }, }, }, @@ -136,23 +138,25 @@ func TestOrgDomainProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.org_domains SET (change_date, sequence, is_primary) = ($1, $2, $3) WHERE (org_id = $4) AND (is_primary = $5)", + expectedStmt: "UPDATE projections.org_domains SET (change_date, sequence, is_primary) = ($1, $2, $3) WHERE (org_id = $4) AND (is_primary = $5) AND (instance_id = $6)", expectedArgs: []interface{}{ anyArg{}, uint64(15), false, "agg-id", true, + "instance-id", }, }, { - expectedStmt: "UPDATE projections.org_domains SET (change_date, sequence, is_primary) = ($1, $2, $3) WHERE (domain = $4) AND (org_id = $5)", + expectedStmt: "UPDATE projections.org_domains SET (change_date, sequence, is_primary) = ($1, $2, $3) WHERE (domain = $4) AND (org_id = $5) AND (instance_id = $6)", expectedArgs: []interface{}{ anyArg{}, uint64(15), true, "domain.new", "agg-id", + "instance-id", }, }, }, @@ -177,10 +181,11 @@ func TestOrgDomainProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.org_domains WHERE (domain = $1) AND (org_id = $2)", + expectedStmt: "DELETE FROM projections.org_domains WHERE (domain = $1) AND (org_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "domain.new", "agg-id", + "instance-id", }, }, }, diff --git a/internal/query/projection/sms.go b/internal/query/projection/sms.go index b4eb9e295a..7b6a78e0c7 100644 --- a/internal/query/projection/sms.go +++ b/internal/query/projection/sms.go @@ -26,6 +26,7 @@ const ( smsTwilioTableSuffix = "twilio" SMSTwilioConfigColumnSMSID = "sms_id" + SMSTwilioColumnInstanceID = "instance_id" SMSTwilioConfigColumnSID = "sid" SMSTwilioConfigColumnSenderNumber = "sender_number" SMSTwilioConfigColumnToken = "token" @@ -50,16 +51,18 @@ func NewSMSConfigProjection(ctx context.Context, config crdb.StatementHandlerCon crdb.NewColumn(SMSColumnResourceOwner, crdb.ColumnTypeText), crdb.NewColumn(SMSColumnInstanceID, crdb.ColumnTypeText), }, - crdb.NewPrimaryKey(SMSColumnInstanceID, SMSColumnID), + crdb.NewPrimaryKey(SMSColumnID, SMSColumnInstanceID), ), crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(SMSTwilioConfigColumnSMSID, crdb.ColumnTypeText, crdb.Default(SMSColumnID)), + crdb.NewColumn(SMSTwilioConfigColumnSMSID, crdb.ColumnTypeText), + crdb.NewColumn(SMSTwilioColumnInstanceID, crdb.ColumnTypeText), crdb.NewColumn(SMSTwilioConfigColumnSID, crdb.ColumnTypeText), crdb.NewColumn(SMSTwilioConfigColumnSenderNumber, crdb.ColumnTypeText), crdb.NewColumn(SMSTwilioConfigColumnToken, crdb.ColumnTypeJSONB), }, crdb.NewPrimaryKey(SMSTwilioConfigColumnSMSID), smsTwilioTableSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_twilio_ref_sms")), ), ) p.StatementHandler = crdb.NewStatementHandler(ctx, config) @@ -108,6 +111,7 @@ func (p *SMSConfigProjection) reduceSMSConfigTwilioAdded(event eventstore.Event) []handler.Column{ handler.NewCol(SMSColumnID, e.ID), handler.NewCol(SMSColumnAggregateID, e.Aggregate().ID), + handler.NewCol(SMSTwilioColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(SMSColumnCreationDate, e.CreationDate()), handler.NewCol(SMSColumnChangeDate, e.CreationDate()), handler.NewCol(SMSColumnResourceOwner, e.Aggregate().ResourceOwner), @@ -119,6 +123,7 @@ func (p *SMSConfigProjection) reduceSMSConfigTwilioAdded(event eventstore.Event) crdb.AddCreateStatement( []handler.Column{ handler.NewCol(SMSTwilioConfigColumnSMSID, e.ID), + handler.NewCol(SMSTwilioColumnInstanceID, e.Aggregate().InstanceID), handler.NewCol(SMSTwilioConfigColumnSID, e.SID), handler.NewCol(SMSTwilioConfigColumnToken, e.Token), handler.NewCol(SMSTwilioConfigColumnSenderNumber, e.SenderNumber), @@ -147,6 +152,7 @@ func (p *SMSConfigProjection) reduceSMSConfigTwilioChanged(event eventstore.Even columns, []handler.Condition{ handler.NewCond(SMSTwilioConfigColumnSMSID, e.ID), + handler.NewCond(SMSTwilioColumnInstanceID, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(smsTwilioTableSuffix), ), @@ -157,6 +163,7 @@ func (p *SMSConfigProjection) reduceSMSConfigTwilioChanged(event eventstore.Even }, []handler.Condition{ handler.NewCond(SMSColumnID, e.ID), + handler.NewCond(SMSColumnInstanceID, e.Aggregate().InstanceID), }, ), ), nil @@ -176,6 +183,7 @@ func (p *SMSConfigProjection) reduceSMSConfigActivated(event eventstore.Event) ( }, []handler.Condition{ handler.NewCond(SMSColumnID, e.ID), + handler.NewCond(SMSColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } @@ -194,6 +202,7 @@ func (p *SMSConfigProjection) reduceSMSConfigDeactivated(event eventstore.Event) }, []handler.Condition{ handler.NewCond(SMSColumnID, e.ID), + handler.NewCond(SMSColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } @@ -207,6 +216,7 @@ func (p *SMSConfigProjection) reduceSMSConfigRemoved(event eventstore.Event) (*h e, []handler.Condition{ handler.NewCond(SMSColumnID, e.ID), + handler.NewCond(SMSColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } diff --git a/internal/query/projection/sms_test.go b/internal/query/projection/sms_test.go index 0b50ced692..9b936e461b 100644 --- a/internal/query/projection/sms_test.go +++ b/internal/query/projection/sms_test.go @@ -54,10 +54,11 @@ func TestSMSProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.sms_configs (id, aggregate_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + expectedStmt: "INSERT INTO projections.sms_configs (id, aggregate_id, instance_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", expectedArgs: []interface{}{ "id", "agg-id", + "instance-id", anyArg{}, anyArg{}, "ro-id", @@ -67,9 +68,10 @@ func TestSMSProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.sms_configs_twilio (sms_id, sid, token, sender_number) VALUES ($1, $2, $3, $4)", + expectedStmt: "INSERT INTO projections.sms_configs_twilio (sms_id, instance_id, sid, token, sender_number) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "id", + "instance-id", "sid", anyArg{}, "sender-number", @@ -101,19 +103,21 @@ func TestSMSProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sms_configs_twilio SET (sid, sender_number) = ($1, $2) WHERE (sms_id = $3)", + expectedStmt: "UPDATE projections.sms_configs_twilio SET (sid, sender_number) = ($1, $2) WHERE (sms_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ &sid, &senderNumber, "id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.sms_configs SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.sms_configs SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "id", + "instance-id", }, }, }, @@ -140,12 +144,13 @@ func TestSMSProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sms_configs SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.sms_configs SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ domain.SMSConfigStateActive, anyArg{}, uint64(15), "id", + "instance-id", }, }, }, @@ -172,12 +177,13 @@ func TestSMSProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.sms_configs SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.sms_configs SET (state, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ domain.SMSConfigStateInactive, anyArg{}, uint64(15), "id", + "instance-id", }, }, }, @@ -204,9 +210,10 @@ func TestSMSProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.sms_configs WHERE (id = $1)", + expectedStmt: "DELETE FROM projections.sms_configs WHERE (id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "id", + "instance-id", }, }, }, diff --git a/internal/query/projection/smtp.go b/internal/query/projection/smtp.go index c64a373a09..f7caab173c 100644 --- a/internal/query/projection/smtp.go +++ b/internal/query/projection/smtp.go @@ -35,7 +35,7 @@ func NewSMTPConfigProjection(ctx context.Context, config crdb.StatementHandlerCo p := new(SMTPConfigProjection) config.ProjectionName = SMTPConfigProjectionTable config.Reducers = p.reducers() - config.InitCheck = crdb.NewMultiTableCheck( + config.InitCheck = crdb.NewTableCheck( crdb.NewTable([]*crdb.Column{ crdb.NewColumn(SMTPConfigColumnAggregateID, crdb.ColumnTypeText), crdb.NewColumn(SMTPConfigColumnCreationDate, crdb.ColumnTypeTimestamp), @@ -132,6 +132,7 @@ func (p *SMTPConfigProjection) reduceSMTPConfigChanged(event eventstore.Event) ( columns, []handler.Condition{ handler.NewCond(SMTPConfigColumnAggregateID, e.Aggregate().ID), + handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } @@ -151,6 +152,7 @@ func (p *SMTPConfigProjection) reduceSMTPConfigPasswordChanged(event eventstore. }, []handler.Condition{ handler.NewCond(SMTPConfigColumnAggregateID, e.Aggregate().ID), + handler.NewCond(SMTPConfigColumnInstanceID, e.Aggregate().InstanceID), }, ), nil } diff --git a/internal/query/projection/smtp_test.go b/internal/query/projection/smtp_test.go index 213bac805d..4c1712d8bf 100644 --- a/internal/query/projection/smtp_test.go +++ b/internal/query/projection/smtp_test.go @@ -45,7 +45,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.smtp_configs SET (change_date, sequence, tls, sender_address, sender_name, host, username) = ($1, $2, $3, $4, $5, $6, $7) WHERE (aggregate_id = $8)", + expectedStmt: "UPDATE projections.smtp_configs SET (change_date, sequence, tls, sender_address, sender_name, host, username) = ($1, $2, $3, $4, $5, $6, $7) WHERE (aggregate_id = $8) AND (instance_id = $9)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -55,6 +55,7 @@ func TestSMTPConfigProjection_reduces(t *testing.T) { "host", "user", "agg-id", + "instance-id", }, }, }, @@ -134,12 +135,13 @@ func TestSMTPConfigProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.smtp_configs SET (change_date, sequence, password) = ($1, $2, $3) WHERE (aggregate_id = $4)", + expectedStmt: "UPDATE projections.smtp_configs SET (change_date, sequence, password) = ($1, $2, $3) WHERE (aggregate_id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, uint64(15), anyArg{}, "agg-id", + "instance-id", }, }, }, diff --git a/internal/query/projection/user.go b/internal/query/projection/user.go index 267285dede..39572dc1df 100644 --- a/internal/query/projection/user.go +++ b/internal/query/projection/user.go @@ -31,8 +31,9 @@ const ( UserUsernameCol = "username" UserTypeCol = "type" - UserHumanSuffix = "humans" - HumanUserIDCol = "user_id" + UserHumanSuffix = "humans" + HumanUserIDCol = "user_id" + HumanUserInstanceIDCol = "instance_id" // profile HumanFirstNameCol = "first_name" @@ -52,10 +53,11 @@ const ( HumanIsPhoneVerifiedCol = "is_phone_verified" // machine - UserMachineSuffix = "machines" - MachineUserIDCol = "user_id" - MachineNameCol = "name" - MachineDescriptionCol = "description" + UserMachineSuffix = "machines" + MachineUserIDCol = "user_id" + MachineUserInstanceIDCol = "instance_id" + MachineNameCol = "name" + MachineDescriptionCol = "description" ) func NewUserProjection(ctx context.Context, config crdb.StatementHandlerConfig) *UserProjection { @@ -74,13 +76,14 @@ func NewUserProjection(ctx context.Context, config crdb.StatementHandlerConfig) crdb.NewColumn(UserUsernameCol, crdb.ColumnTypeText), crdb.NewColumn(UserTypeCol, crdb.ColumnTypeEnum), }, - crdb.NewPrimaryKey(UserInstanceIDCol, UserIDCol), + crdb.NewPrimaryKey(UserIDCol, UserInstanceIDCol), crdb.WithIndex(crdb.NewIndex("username_idx", []string{UserUsernameCol})), crdb.WithIndex(crdb.NewIndex("ro_idx", []string{UserResourceOwnerCol})), crdb.WithConstraint(crdb.NewConstraint("id_unique", []string{UserIDCol})), ), crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(HumanUserIDCol, crdb.ColumnTypeText, crdb.DeleteCascade(UserIDCol)), + crdb.NewColumn(HumanUserIDCol, crdb.ColumnTypeText), + crdb.NewColumn(HumanUserInstanceIDCol, crdb.ColumnTypeText), crdb.NewColumn(HumanFirstNameCol, crdb.ColumnTypeText), crdb.NewColumn(HumanLastNameCol, crdb.ColumnTypeText), crdb.NewColumn(HumanNickNameCol, crdb.ColumnTypeText, crdb.Nullable()), @@ -93,16 +96,19 @@ func NewUserProjection(ctx context.Context, config crdb.StatementHandlerConfig) crdb.NewColumn(HumanPhoneCol, crdb.ColumnTypeText, crdb.Nullable()), crdb.NewColumn(HumanIsPhoneVerifiedCol, crdb.ColumnTypeBool, crdb.Nullable()), }, - crdb.NewPrimaryKey(HumanUserIDCol), + crdb.NewPrimaryKey(HumanUserIDCol, HumanUserInstanceIDCol), UserHumanSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_human_ref_user")), ), crdb.NewSuffixedTable([]*crdb.Column{ - crdb.NewColumn(MachineUserIDCol, crdb.ColumnTypeText, crdb.DeleteCascade(UserIDCol)), + crdb.NewColumn(MachineUserIDCol, crdb.ColumnTypeText), + crdb.NewColumn(MachineUserInstanceIDCol, crdb.ColumnTypeText), crdb.NewColumn(MachineNameCol, crdb.ColumnTypeText), crdb.NewColumn(MachineDescriptionCol, crdb.ColumnTypeText, crdb.Nullable()), }, - crdb.NewPrimaryKey(MachineUserIDCol), + crdb.NewPrimaryKey(MachineUserIDCol, MachineUserInstanceIDCol), UserMachineSuffix, + crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys("fk_machine_ref_user")), ), ) p.StatementHandler = crdb.NewStatementHandler(ctx, config) @@ -262,6 +268,7 @@ func (p *UserProjection) reduceHumanAdded(event eventstore.Event) (*handler.Stat crdb.AddCreateStatement( []handler.Column{ handler.NewCol(HumanUserIDCol, e.Aggregate().ID), + handler.NewCol(HumanUserInstanceIDCol, e.Aggregate().InstanceID), handler.NewCol(HumanFirstNameCol, e.FirstName), handler.NewCol(HumanLastNameCol, e.LastName), handler.NewCol(HumanNickNameCol, &sql.NullString{String: e.NickName, Valid: e.NickName != ""}), @@ -299,6 +306,7 @@ func (p *UserProjection) reduceHumanRegistered(event eventstore.Event) (*handler crdb.AddCreateStatement( []handler.Column{ handler.NewCol(HumanUserIDCol, e.Aggregate().ID), + handler.NewCol(HumanUserInstanceIDCol, e.Aggregate().InstanceID), handler.NewCol(HumanFirstNameCol, e.FirstName), handler.NewCol(HumanLastNameCol, e.LastName), handler.NewCol(HumanNickNameCol, &sql.NullString{String: e.NickName, Valid: e.NickName != ""}), @@ -325,6 +333,7 @@ func (p *UserProjection) reduceHumanInitCodeAdded(event eventstore.Event) (*hand }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, ), nil } @@ -341,6 +350,7 @@ func (p *UserProjection) reduceHumanInitCodeSucceeded(event eventstore.Event) (* }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, ), nil } @@ -360,6 +370,7 @@ func (p *UserProjection) reduceUserLocked(event eventstore.Event) (*handler.Stat }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), nil } @@ -379,6 +390,7 @@ func (p *UserProjection) reduceUserUnlocked(event eventstore.Event) (*handler.St }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), nil } @@ -398,6 +410,7 @@ func (p *UserProjection) reduceUserDeactivated(event eventstore.Event) (*handler }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), nil } @@ -417,6 +430,7 @@ func (p *UserProjection) reduceUserReactivated(event eventstore.Event) (*handler }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), nil } @@ -431,6 +445,7 @@ func (p *UserProjection) reduceUserRemoved(event eventstore.Event) (*handler.Sta e, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), nil } @@ -450,6 +465,7 @@ func (p *UserProjection) reduceUserNameChanged(event eventstore.Event) (*handler }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), nil } @@ -493,12 +509,14 @@ func (p *UserProjection) reduceHumanProfileChanged(event eventstore.Event) (*han }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), crdb.AddUpdateStatement( cols, []handler.Condition{ handler.NewCond(HumanUserIDCol, e.Aggregate().ID), + handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(UserHumanSuffix), ), @@ -520,6 +538,7 @@ func (p *UserProjection) reduceHumanPhoneChanged(event eventstore.Event) (*handl }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), crdb.AddUpdateStatement( @@ -529,6 +548,7 @@ func (p *UserProjection) reduceHumanPhoneChanged(event eventstore.Event) (*handl }, []handler.Condition{ handler.NewCond(HumanUserIDCol, e.Aggregate().ID), + handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(UserHumanSuffix), ), @@ -550,6 +570,7 @@ func (p *UserProjection) reduceHumanPhoneRemoved(event eventstore.Event) (*handl }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), crdb.AddUpdateStatement( @@ -559,6 +580,7 @@ func (p *UserProjection) reduceHumanPhoneRemoved(event eventstore.Event) (*handl }, []handler.Condition{ handler.NewCond(HumanUserIDCol, e.Aggregate().ID), + handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(UserHumanSuffix), ), @@ -580,6 +602,7 @@ func (p *UserProjection) reduceHumanPhoneVerified(event eventstore.Event) (*hand }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), crdb.AddUpdateStatement( @@ -588,6 +611,7 @@ func (p *UserProjection) reduceHumanPhoneVerified(event eventstore.Event) (*hand }, []handler.Condition{ handler.NewCond(HumanUserIDCol, e.Aggregate().ID), + handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(UserHumanSuffix), ), @@ -609,6 +633,7 @@ func (p *UserProjection) reduceHumanEmailChanged(event eventstore.Event) (*handl }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), crdb.AddUpdateStatement( @@ -618,6 +643,7 @@ func (p *UserProjection) reduceHumanEmailChanged(event eventstore.Event) (*handl }, []handler.Condition{ handler.NewCond(HumanUserIDCol, e.Aggregate().ID), + handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(UserHumanSuffix), ), @@ -639,6 +665,7 @@ func (p *UserProjection) reduceHumanEmailVerified(event eventstore.Event) (*hand }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), crdb.AddUpdateStatement( @@ -647,6 +674,7 @@ func (p *UserProjection) reduceHumanEmailVerified(event eventstore.Event) (*hand }, []handler.Condition{ handler.NewCond(HumanUserIDCol, e.Aggregate().ID), + handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(UserHumanSuffix), ), @@ -668,6 +696,7 @@ func (p *UserProjection) reduceHumanAvatarAdded(event eventstore.Event) (*handle }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), crdb.AddUpdateStatement( @@ -676,6 +705,7 @@ func (p *UserProjection) reduceHumanAvatarAdded(event eventstore.Event) (*handle }, []handler.Condition{ handler.NewCond(HumanUserIDCol, e.Aggregate().ID), + handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(UserHumanSuffix), ), @@ -697,6 +727,7 @@ func (p *UserProjection) reduceHumanAvatarRemoved(event eventstore.Event) (*hand }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), crdb.AddUpdateStatement( @@ -705,6 +736,7 @@ func (p *UserProjection) reduceHumanAvatarRemoved(event eventstore.Event) (*hand }, []handler.Condition{ handler.NewCond(HumanUserIDCol, e.Aggregate().ID), + handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(UserHumanSuffix), ), @@ -735,6 +767,7 @@ func (p *UserProjection) reduceMachineAdded(event eventstore.Event) (*handler.St crdb.AddCreateStatement( []handler.Column{ handler.NewCol(MachineUserIDCol, e.Aggregate().ID), + handler.NewCol(MachineUserInstanceIDCol, e.Aggregate().InstanceID), handler.NewCol(MachineNameCol, e.Name), handler.NewCol(MachineDescriptionCol, &sql.NullString{String: e.Description, Valid: e.Description != ""}), }, @@ -769,12 +802,14 @@ func (p *UserProjection) reduceMachineChanged(event eventstore.Event) (*handler. }, []handler.Condition{ handler.NewCond(UserIDCol, e.Aggregate().ID), + handler.NewCond(UserInstanceIDCol, e.Aggregate().InstanceID), }, ), crdb.AddUpdateStatement( cols, []handler.Condition{ handler.NewCond(MachineUserIDCol, e.Aggregate().ID), + handler.NewCond(MachineUserInstanceIDCol, e.Aggregate().InstanceID), }, crdb.WithTableSuffix(UserMachineSuffix), ), diff --git a/internal/query/projection/user_test.go b/internal/query/projection/user_test.go index 554f0262d3..efa0a6565c 100644 --- a/internal/query/projection/user_test.go +++ b/internal/query/projection/user_test.go @@ -64,9 +64,10 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users_humans (user_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", expectedArgs: []interface{}{ "agg-id", + "instance-id", "first-name", "last-name", &sql.NullString{String: "nick-name", Valid: true}, @@ -123,9 +124,10 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users_humans (user_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", expectedArgs: []interface{}{ "agg-id", + "instance-id", "first-name", "last-name", &sql.NullString{String: "nick-name", Valid: true}, @@ -177,9 +179,10 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users_humans (user_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", expectedArgs: []interface{}{ "agg-id", + "instance-id", "first-name", "last-name", &sql.NullString{}, @@ -236,9 +239,10 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users_humans (user_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", expectedArgs: []interface{}{ "agg-id", + "instance-id", "first-name", "last-name", &sql.NullString{String: "nick-name", Valid: true}, @@ -295,9 +299,10 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users_humans (user_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", expectedArgs: []interface{}{ "agg-id", + "instance-id", "first-name", "last-name", &sql.NullString{String: "nick-name", Valid: true}, @@ -349,9 +354,10 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users_humans (user_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)", expectedArgs: []interface{}{ "agg-id", + "instance-id", "first-name", "last-name", &sql.NullString{}, @@ -384,10 +390,11 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (state) = ($1) WHERE (id = $2)", + expectedStmt: "UPDATE projections.users SET (state) = ($1) WHERE (id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ domain.UserStateInitial, "agg-id", + "instance-id", }, }, }, @@ -412,10 +419,11 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (state) = ($1) WHERE (id = $2)", + expectedStmt: "UPDATE projections.users SET (state) = ($1) WHERE (id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ domain.UserStateInitial, "agg-id", + "instance-id", }, }, }, @@ -440,10 +448,11 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (state) = ($1) WHERE (id = $2)", + expectedStmt: "UPDATE projections.users SET (state) = ($1) WHERE (id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ domain.UserStateActive, "agg-id", + "instance-id", }, }, }, @@ -468,10 +477,11 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (state) = ($1) WHERE (id = $2)", + expectedStmt: "UPDATE projections.users SET (state) = ($1) WHERE (id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ domain.UserStateActive, "agg-id", + "instance-id", }, }, }, @@ -496,12 +506,13 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.users SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, domain.UserStateLocked, uint64(15), "agg-id", + "instance-id", }, }, }, @@ -526,12 +537,13 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.users SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, domain.UserStateActive, uint64(15), "agg-id", + "instance-id", }, }, }, @@ -556,12 +568,13 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.users SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, domain.UserStateInactive, uint64(15), "agg-id", + "instance-id", }, }, }, @@ -586,12 +599,13 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.users SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, domain.UserStateActive, uint64(15), "agg-id", + "instance-id", }, }, }, @@ -616,9 +630,10 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.users WHERE (id = $1)", + expectedStmt: "DELETE FROM projections.users WHERE (id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", + "instance-id", }, }, }, @@ -645,12 +660,13 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, username, sequence) = ($1, $2, $3) WHERE (id = $4)", + expectedStmt: "UPDATE projections.users SET (change_date, username, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, "username", uint64(15), "agg-id", + "instance-id", }, }, }, @@ -682,15 +698,16 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (first_name, last_name, nick_name, display_name, preferred_language, gender) = ($1, $2, $3, $4, $5, $6) WHERE (user_id = $7)", + expectedStmt: "UPDATE projections.users_humans SET (first_name, last_name, nick_name, display_name, preferred_language, gender) = ($1, $2, $3, $4, $5, $6) WHERE (user_id = $7) AND (instance_id = $8)", expectedArgs: []interface{}{ "first-name", "last-name", @@ -699,6 +716,7 @@ func TestUserProjection_reduces(t *testing.T) { "ch-DE", domain.GenderDiverse, "agg-id", + "instance-id", }, }, }, @@ -730,15 +748,16 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (first_name, last_name, nick_name, display_name, preferred_language, gender) = ($1, $2, $3, $4, $5, $6) WHERE (user_id = $7)", + expectedStmt: "UPDATE projections.users_humans SET (first_name, last_name, nick_name, display_name, preferred_language, gender) = ($1, $2, $3, $4, $5, $6) WHERE (user_id = $7) AND (instance_id = $8)", expectedArgs: []interface{}{ "first-name", "last-name", @@ -747,6 +766,7 @@ func TestUserProjection_reduces(t *testing.T) { "ch-DE", domain.GenderDiverse, "agg-id", + "instance-id", }, }, }, @@ -773,19 +793,21 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3)", + expectedStmt: "UPDATE projections.users_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ "+41 00 000 00 00", false, "agg-id", + "instance-id", }, }, }, @@ -812,19 +834,21 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3)", + expectedStmt: "UPDATE projections.users_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ "+41 00 000 00 00", false, "agg-id", + "instance-id", }, }, }, @@ -849,19 +873,21 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3)", + expectedStmt: "UPDATE projections.users_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ nil, nil, "agg-id", + "instance-id", }, }, }, @@ -886,19 +912,21 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3)", + expectedStmt: "UPDATE projections.users_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ nil, nil, "agg-id", + "instance-id", }, }, }, @@ -923,18 +951,20 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (is_phone_verified) = ($1) WHERE (user_id = $2)", + expectedStmt: "UPDATE projections.users_humans SET (is_phone_verified) = ($1) WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ true, "agg-id", + "instance-id", }, }, }, @@ -959,18 +989,20 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (is_phone_verified) = ($1) WHERE (user_id = $2)", + expectedStmt: "UPDATE projections.users_humans SET (is_phone_verified) = ($1) WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ true, "agg-id", + "instance-id", }, }, }, @@ -997,19 +1029,21 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3)", + expectedStmt: "UPDATE projections.users_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ "email@zitadel.com", false, "agg-id", + "instance-id", }, }, }, @@ -1036,19 +1070,21 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3)", + expectedStmt: "UPDATE projections.users_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ "email@zitadel.com", false, "agg-id", + "instance-id", }, }, }, @@ -1073,18 +1109,20 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (is_email_verified) = ($1) WHERE (user_id = $2)", + expectedStmt: "UPDATE projections.users_humans SET (is_email_verified) = ($1) WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ true, "agg-id", + "instance-id", }, }, }, @@ -1109,18 +1147,20 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (is_email_verified) = ($1) WHERE (user_id = $2)", + expectedStmt: "UPDATE projections.users_humans SET (is_email_verified) = ($1) WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ true, "agg-id", + "instance-id", }, }, }, @@ -1147,18 +1187,20 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (avatar_key) = ($1) WHERE (user_id = $2)", + expectedStmt: "UPDATE projections.users_humans SET (avatar_key) = ($1) WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "users/agg-id/avatar", "agg-id", + "instance-id", }, }, }, @@ -1183,18 +1225,20 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_humans SET (avatar_key) = ($1) WHERE (user_id = $2)", + expectedStmt: "UPDATE projections.users_humans SET (avatar_key) = ($1) WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ nil, "agg-id", + "instance-id", }, }, }, @@ -1236,9 +1280,10 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users_machines (user_id, name, description) VALUES ($1, $2, $3)", + expectedStmt: "INSERT INTO projections.users_machines (user_id, instance_id, name, description) VALUES ($1, $2, $3, $4)", expectedArgs: []interface{}{ "agg-id", + "instance-id", "machine-name", &sql.NullString{}, }, @@ -1283,9 +1328,10 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users_machines (user_id, name, description) VALUES ($1, $2, $3)", + expectedStmt: "INSERT INTO projections.users_machines (user_id, instance_id, name, description) VALUES ($1, $2, $3, $4)", expectedArgs: []interface{}{ "agg-id", + "instance-id", "machine-name", &sql.NullString{String: "description", Valid: true}, }, @@ -1315,19 +1361,21 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_machines SET (name, description) = ($1, $2) WHERE (user_id = $3)", + expectedStmt: "UPDATE projections.users_machines SET (name, description) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ "machine-name", "description", "agg-id", + "instance-id", }, }, }, @@ -1354,18 +1402,20 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_machines SET (name) = ($1) WHERE (user_id = $2)", + expectedStmt: "UPDATE projections.users_machines SET (name) = ($1) WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "machine-name", "agg-id", + "instance-id", }, }, }, @@ -1392,18 +1442,20 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3)", + expectedStmt: "UPDATE projections.users SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), "agg-id", + "instance-id", }, }, { - expectedStmt: "UPDATE projections.users_machines SET (description) = ($1) WHERE (user_id = $2)", + expectedStmt: "UPDATE projections.users_machines SET (description) = ($1) WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "description", "agg-id", + "instance-id", }, }, }, diff --git a/proto/zitadel/system.proto b/proto/zitadel/system.proto index 523fe2df28..c0d7d98c47 100644 --- a/proto/zitadel/system.proto +++ b/proto/zitadel/system.proto @@ -203,9 +203,6 @@ service SystemService { post: "/views/{database}/{view_name}"; }; - option (zitadel.v1.auth_option) = { - permission: "iam.write"; - }; option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { tags: "views";