diff --git a/backend/v3/domain/id_provider.go b/backend/v3/domain/id_provider.go index 6f2bab1e3..dbcfbbc88 100644 --- a/backend/v3/domain/id_provider.go +++ b/backend/v3/domain/id_provider.go @@ -296,6 +296,8 @@ type idProviderColumns interface { } type idProviderConditions interface { + // PrimaryKeyCondition returns a filter on the primary key fields. + PrimaryKeyCondition(instanceID, idpID string) database.Condition InstanceIDCondition(id string) database.Condition OrgIDCondition(id *string) database.Condition IDCondition(id string) IDPIdentifierCondition @@ -327,6 +329,8 @@ type idProviderChanges interface { } type IDProviderRepository interface { + Repository + idProviderColumns idProviderConditions idProviderChanges diff --git a/backend/v3/domain/instance.go b/backend/v3/domain/instance.go index 446331a32..60c6381ba 100644 --- a/backend/v3/domain/instance.go +++ b/backend/v3/domain/instance.go @@ -65,6 +65,8 @@ type instanceColumns interface { // instanceConditions define all the conditions for the instance table. type instanceConditions interface { + // PrimaryKeyCondition returns a filter on the primary key fields. + PrimaryKeyCondition(instanceID string) database.Condition // IDCondition returns an equal filter on the id field. IDCondition(instanceID string) database.Condition // NameCondition returns a filter on the name field. @@ -93,6 +95,8 @@ type instanceChanges interface { // InstanceRepository is the interface for the instance repository. type InstanceRepository interface { + Repository + instanceColumns instanceConditions instanceChanges diff --git a/backend/v3/domain/instance_domain.go b/backend/v3/domain/instance_domain.go index 786be287e..5a9499f33 100644 --- a/backend/v3/domain/instance_domain.go +++ b/backend/v3/domain/instance_domain.go @@ -47,6 +47,8 @@ type instanceDomainColumns interface { type instanceDomainConditions interface { domainConditions + // PrimaryKeyCondition returns a filter on the primary key fields. + PrimaryKeyCondition(domain string) database.Condition // TypeCondition returns a filter for the type field. TypeCondition(typ DomainType) database.Condition } @@ -58,6 +60,8 @@ type instanceDomainChanges interface { } type InstanceDomainRepository interface { + Repository + instanceDomainColumns instanceDomainConditions instanceDomainChanges diff --git a/backend/v3/domain/organization.go b/backend/v3/domain/organization.go index cc99bebc9..d9b839c4e 100644 --- a/backend/v3/domain/organization.go +++ b/backend/v3/domain/organization.go @@ -51,8 +51,10 @@ type organizationColumns interface { // organizationConditions define all the conditions for the instance table. type organizationConditions interface { + // PrimaryKeyCondition returns a filter on the primary key fields. + PrimaryKeyCondition(instanceID, organizationID string) database.Condition // IDCondition returns an equal filter on the id field. - IDCondition(instanceID string) database.Condition + IDCondition(orgID string) database.Condition // NameCondition returns a filter on the name field. NameCondition(op database.TextOperation, name string) database.Condition // InstanceIDCondition returns a filter on the instance id field. @@ -77,6 +79,8 @@ type organizationChanges interface { // OrganizationRepository is the interface for the instance repository. type OrganizationRepository interface { + Repository + organizationColumns organizationConditions organizationChanges diff --git a/backend/v3/domain/organization_domain.go b/backend/v3/domain/organization_domain.go index 157e2c890..3d61914cc 100644 --- a/backend/v3/domain/organization_domain.go +++ b/backend/v3/domain/organization_domain.go @@ -47,6 +47,9 @@ type organizationDomainColumns interface { type organizationDomainConditions interface { domainConditions + + // PrimaryKeyCondition returns a filter on the primary key fields. + PrimaryKeyCondition(instanceID, orgID, domain string) database.Condition // OrgIDCondition returns a filter on the org id field. OrgIDCondition(orgID string) database.Condition // IsVerifiedCondition returns a filter on the is verified field. @@ -65,6 +68,8 @@ type organizationDomainChanges interface { //go:generate mockgen -typed -package domainmock -destination ./mock/org_domain.mock.go . OrganizationDomainRepository type OrganizationDomainRepository interface { + Repository + organizationDomainColumns organizationDomainConditions organizationDomainChanges diff --git a/backend/v3/domain/project.go b/backend/v3/domain/project.go index f0d9de39d..dad502423 100644 --- a/backend/v3/domain/project.go +++ b/backend/v3/domain/project.go @@ -30,8 +30,6 @@ type Project struct { } type projectColumns interface { - // PrimaryKeyColumns returns the columns for the primary key fields - PrimaryKeyColumns() []database.Column // InstanceIDColumn returns the column for the instance id field InstanceIDColumn() database.Column // OrganizationIDColumn returns the column for the organization id field @@ -94,6 +92,8 @@ type projectChanges interface { // //go:generate mockgen -typed -package domainmock -destination ./mock/project.mock.go . ProjectRepository type ProjectRepository interface { + Repository + projectColumns projectConditions projectChanges diff --git a/backend/v3/domain/repository.go b/backend/v3/domain/repository.go new file mode 100644 index 000000000..3ed7b7200 --- /dev/null +++ b/backend/v3/domain/repository.go @@ -0,0 +1,9 @@ +package domain + +import "github.com/zitadel/zitadel/backend/v3/storage/database" + +// Repository is the base interface for all repositories. +type Repository interface { + // PrimaryKeyColumns returns the columns for the primary key fields + PrimaryKeyColumns() []database.Column +} diff --git a/backend/v3/domain/user.go b/backend/v3/domain/user.go index 333b5c96f..cb6527577 100644 --- a/backend/v3/domain/user.go +++ b/backend/v3/domain/user.go @@ -27,6 +27,8 @@ type userColumns interface { // userConditions define all the conditions for the user table. type userConditions interface { + // PrimaryKeyCondition returns a filter on the primary key fields. + PrimaryKeyCondition(instanceID, userID string) database.Condition // InstanceIDCondition returns an equal filter on the instance id field. InstanceIDCondition(instanceID string) database.Condition // OrgIDCondition returns an equal filter on the org id field. @@ -53,6 +55,8 @@ type userChanges interface { // UserRepository is the interface for the user repository. type UserRepository interface { + Repository + userColumns userConditions userChanges diff --git a/backend/v3/storage/database/repository/id_provider.go b/backend/v3/storage/database/repository/id_provider.go index 9f92de35e..54decf427 100644 --- a/backend/v3/storage/database/repository/id_provider.go +++ b/backend/v3/storage/database/repository/id_provider.go @@ -431,6 +431,14 @@ func (i *idProvider) GetSAML(ctx context.Context, client database.QueryExecutor, // columns // ------------------------------------------------------------- +// PrimaryKeyColumns implements [domain.Repository]. +func (i idProvider) PrimaryKeyColumns() []database.Column { + return []database.Column{ + i.InstanceIDColumn(), + i.IDColumn(), + } +} + func (idProvider) InstanceIDColumn() database.Column { return database.NewColumn("identity_providers", "instance_id") } @@ -499,6 +507,13 @@ func (idProvider) UpdatedAtColumn() database.Column { // conditions // ------------------------------------------------------------- +func (i idProvider) PrimaryKeyCondition(instanceID, id string) database.Condition { + return database.And( + i.InstanceIDCondition(instanceID), + i.IDCondition(id), + ) +} + func (i idProvider) InstanceIDCondition(id string) database.Condition { return database.NewTextCondition(i.InstanceIDColumn(), database.TextOperationEqual, id) } diff --git a/backend/v3/storage/database/repository/instance.go b/backend/v3/storage/database/repository/instance.go index 2d03bdc5e..ff314e29e 100644 --- a/backend/v3/storage/database/repository/instance.go +++ b/backend/v3/storage/database/repository/instance.go @@ -166,6 +166,10 @@ func (i instance) SetConsoleAppID(id string) database.Change { // conditions // ------------------------------------------------------------- +func (i instance) PrimaryKeyCondition(instanceID string) database.Condition { + return i.IDCondition(instanceID) +} + // IDCondition implements [domain.instanceConditions]. func (i instance) IDCondition(id string) database.Condition { return database.NewTextCondition(i.IDColumn(), database.TextOperationEqual, id) @@ -204,6 +208,11 @@ func (i instance) ExistsDomain(cond database.Condition) database.Condition { // columns // ------------------------------------------------------------- +// PrimaryKeyColumns implements [domain.Repository]. +func (i instance) PrimaryKeyColumns() []database.Column { + return []database.Column{i.IDColumn()} +} + // IDColumn implements [domain.instanceColumns]. func (i instance) IDColumn() database.Column { return database.NewColumn(i.unqualifiedTableName(), "id") diff --git a/backend/v3/storage/database/repository/instance_domain.go b/backend/v3/storage/database/repository/instance_domain.go index 9cb4d9e12..5edaf0af6 100644 --- a/backend/v3/storage/database/repository/instance_domain.go +++ b/backend/v3/storage/database/repository/instance_domain.go @@ -136,6 +136,10 @@ func (i instanceDomain) SetType(typ domain.DomainType) database.Change { // conditions // ------------------------------------------------------------- +func (i instanceDomain) PrimaryKeyCondition(domain string) database.Condition { + return i.DomainCondition(database.TextOperationEqual, domain) +} + // DomainCondition implements [domain.InstanceDomainRepository]. func (i instanceDomain) DomainCondition(op database.TextOperation, domain string) database.Condition { return database.NewTextCondition(i.DomainColumn(), op, domain) @@ -160,6 +164,13 @@ func (i instanceDomain) TypeCondition(typ domain.DomainType) database.Condition // columns // ------------------------------------------------------------- +// PrimaryKeyColumns implements [domain.Repository]. +func (i instanceDomain) PrimaryKeyColumns() []database.Column { + return []database.Column{ + i.DomainColumn(), + } +} + // CreatedAtColumn implements [domain.InstanceDomainRepository]. func (i instanceDomain) CreatedAtColumn() database.Column { return database.NewColumn(i.unqualifiedTableName(), "created_at") diff --git a/backend/v3/storage/database/repository/org.go b/backend/v3/storage/database/repository/org.go index 5f07a9d6b..d06ac87d6 100644 --- a/backend/v3/storage/database/repository/org.go +++ b/backend/v3/storage/database/repository/org.go @@ -148,6 +148,13 @@ func (o org) SetState(state domain.OrgState) database.Change { // conditions // ------------------------------------------------------------- +func (o org) PrimaryKeyCondition(instanceID, orgID string) database.Condition { + return database.And( + o.InstanceIDCondition(instanceID), + o.IDCondition(orgID), + ) +} + // IDCondition implements [domain.organizationConditions]. func (o org) IDCondition(id string) database.Condition { return database.NewTextCondition(o.IDColumn(), database.TextOperationEqual, id) @@ -221,6 +228,14 @@ func (o org) ExistsMetadata(cond database.Condition) database.Condition { // columns // ------------------------------------------------------------- +// PrimaryKeyColumns implements [domain.Repository]. +func (o org) PrimaryKeyColumns() []database.Column { + return []database.Column{ + o.InstanceIDColumn(), + o.IDColumn(), + } +} + // IDColumn implements [domain.organizationColumns]. func (o org) IDColumn() database.Column { return database.NewColumn(o.unqualifiedTableName(), "id") diff --git a/backend/v3/storage/database/repository/org_domain.go b/backend/v3/storage/database/repository/org_domain.go index 4d73647ed..2ecaf0961 100644 --- a/backend/v3/storage/database/repository/org_domain.go +++ b/backend/v3/storage/database/repository/org_domain.go @@ -157,6 +157,14 @@ func (o orgDomain) SetUpdatedAt(updatedAt time.Time) database.Change { // conditions // ------------------------------------------------------------- +func (o orgDomain) PrimaryKeyCondition(instanceID, orgID, domain string) database.Condition { + return database.And( + o.InstanceIDCondition(instanceID), + o.OrgIDCondition(orgID), + o.DomainCondition(database.TextOperationEqual, domain), + ) +} + // DomainCondition implements [domain.OrganizationDomainRepository]. func (o orgDomain) DomainCondition(op database.TextOperation, domain string) database.Condition { return database.NewTextCondition(o.DomainColumn(), op, domain) @@ -187,6 +195,15 @@ func (o orgDomain) OrgIDCondition(orgID string) database.Condition { // columns // ------------------------------------------------------------- +// PrimaryKeyColumns implements [domain.Repository]. +func (o orgDomain) PrimaryKeyColumns() []database.Column { + return []database.Column{ + o.InstanceIDColumn(), + o.OrgIDColumn(), + o.DomainColumn(), + } +} + // CreatedAtColumn implements [domain.OrganizationDomainRepository]. // Subtle: this method shadows the method ([domain.OrganizationRepository]).CreatedAtColumn of orgDomain.org. func (o orgDomain) CreatedAtColumn() database.Column { diff --git a/backend/v3/storage/database/repository/repository.go b/backend/v3/storage/database/repository/repository.go index 95a27c2b6..065a9fcc8 100644 --- a/backend/v3/storage/database/repository/repository.go +++ b/backend/v3/storage/database/repository/repository.go @@ -3,6 +3,7 @@ package repository import ( "context" + "github.com/zitadel/zitadel/backend/v3/domain" "github.com/zitadel/zitadel/backend/v3/storage/database" ) @@ -29,14 +30,10 @@ func checkRestrictingColumns( return nil } -type pkRepository interface { - PrimaryKeyColumns() []database.Column -} - // checkPKCondition checks if the Primary Key columns are part of the condition. // This can ensure only a single row is affected by updates and deletes. func checkPKCondition( - repo pkRepository, + repo domain.Repository, condition database.Condition, ) error { return checkRestrictingColumns( diff --git a/backend/v3/storage/database/repository/user.go b/backend/v3/storage/database/repository/user.go index 8527767c4..c6e15a72c 100644 --- a/backend/v3/storage/database/repository/user.go +++ b/backend/v3/storage/database/repository/user.go @@ -135,6 +135,13 @@ func (u user) SetUsername(username string) database.Change { // conditions // ------------------------------------------------------------- +func (u user) PrimaryKeyCondition(instanceID, userID string) database.Condition { + return database.And( + u.InstanceIDCondition(instanceID), + u.IDCondition(userID), + ) +} + // InstanceIDCondition implements [domain.userConditions]. func (u user) InstanceIDCondition(instanceID string) database.Condition { return database.NewTextCondition(u.InstanceIDColumn(), database.TextOperationEqual, instanceID) @@ -182,6 +189,14 @@ func (u user) DeletedAtCondition(op database.NumberOperation, deletedAt time.Tim // columns // ------------------------------------------------------------- +// PrimaryKeyColumns implements [domain.Repository]. +func (u user) PrimaryKeyColumns() []database.Column { + return []database.Column{ + u.InstanceIDColumn(), + u.IDColumn(), + } +} + // InstanceIDColumn implements [domain.userColumns]. func (user) InstanceIDColumn() database.Column { return database.NewColumn("users", "instance_id") diff --git a/internal/query/projection/instance_domain_relational.go b/internal/query/projection/instance_domain_relational.go index 80606fda6..1c688f6e0 100644 --- a/internal/query/projection/instance_domain_relational.go +++ b/internal/query/projection/instance_domain_relational.go @@ -92,8 +92,8 @@ func (p *instanceDomainRelationalProjection) reduceDomainPrimarySet(event events _, err := domainRepo.Update(ctx, v3_sql.SQLTx(tx), database.And( + domainRepo.PrimaryKeyCondition(e.Domain), domainRepo.InstanceIDCondition(e.Aggregate().InstanceID), - domainRepo.DomainCondition(database.TextOperationEqual, e.Domain), domainRepo.TypeCondition(domain.DomainTypeCustom), ), domainRepo.SetPrimary(), @@ -116,8 +116,8 @@ func (p *instanceDomainRelationalProjection) reduceCustomDomainRemoved(event eve domainRepo := repository.InstanceDomainRepository() _, err := domainRepo.Remove(ctx, v3_sql.SQLTx(tx), database.And( + domainRepo.PrimaryKeyCondition(e.Domain), domainRepo.InstanceIDCondition(e.Aggregate().InstanceID), - domainRepo.DomainCondition(database.TextOperationEqual, e.Domain), domainRepo.TypeCondition(domain.DomainTypeCustom), ), ) @@ -158,8 +158,8 @@ func (p *instanceDomainRelationalProjection) reduceTrustedDomainRemoved(event ev domainRepo := repository.InstanceDomainRepository() _, err := domainRepo.Remove(ctx, v3_sql.SQLTx(tx), database.And( + domainRepo.PrimaryKeyCondition(e.Domain), domainRepo.InstanceIDCondition(e.Aggregate().InstanceID), - domainRepo.DomainCondition(database.TextOperationEqual, e.Domain), domainRepo.TypeCondition(domain.DomainTypeTrusted), ), ) diff --git a/internal/query/projection/org_domain_relational.go b/internal/query/projection/org_domain_relational.go index 2ac3dc449..754969f05 100644 --- a/internal/query/projection/org_domain_relational.go +++ b/internal/query/projection/org_domain_relational.go @@ -5,7 +5,6 @@ import ( "database/sql" "github.com/zitadel/zitadel/backend/v3/domain" - "github.com/zitadel/zitadel/backend/v3/storage/database" v3_sql "github.com/zitadel/zitadel/backend/v3/storage/database/dialect/sql" "github.com/zitadel/zitadel/backend/v3/storage/database/repository" old_domain "github.com/zitadel/zitadel/internal/domain" @@ -87,11 +86,7 @@ func (p *orgDomainRelationalProjection) reducePrimarySet(event eventstore.Event) } domainRepo := repository.OrganizationDomainRepository() _, err := domainRepo.Update(ctx, v3_sql.SQLTx(tx), - database.And( - domainRepo.InstanceIDCondition(e.Aggregate().InstanceID), - domainRepo.OrgIDCondition(e.Aggregate().ResourceOwner), - domainRepo.DomainCondition(database.TextOperationEqual, e.Domain), - ), + domainRepo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ResourceOwner, e.Domain), domainRepo.SetPrimary(), domainRepo.SetUpdatedAt(e.CreationDate()), ) @@ -111,11 +106,7 @@ func (p *orgDomainRelationalProjection) reduceRemoved(event eventstore.Event) (* } domainRepo := repository.OrganizationDomainRepository() _, err := domainRepo.Remove(ctx, v3_sql.SQLTx(tx), - database.And( - domainRepo.InstanceIDCondition(e.Aggregate().InstanceID), - domainRepo.OrgIDCondition(e.Aggregate().ResourceOwner), - domainRepo.DomainCondition(database.TextOperationEqual, e.Domain), - ), + domainRepo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ResourceOwner, e.Domain), ) return err }), nil @@ -143,11 +134,7 @@ func (p *orgDomainRelationalProjection) reduceVerificationAdded(event eventstore domainRepo := repository.OrganizationDomainRepository() _, err := domainRepo.Update(ctx, v3_sql.SQLTx(tx), - database.And( - domainRepo.InstanceIDCondition(e.Aggregate().InstanceID), - domainRepo.OrgIDCondition(e.Aggregate().ResourceOwner), - domainRepo.DomainCondition(database.TextOperationEqual, e.Domain), - ), + domainRepo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ResourceOwner, e.Domain), domainRepo.SetValidationType(validationType), domainRepo.SetUpdatedAt(e.CreationDate()), ) @@ -168,11 +155,7 @@ func (p *orgDomainRelationalProjection) reduceVerified(event eventstore.Event) ( domainRepo := repository.OrganizationDomainRepository() _, err := domainRepo.Update(ctx, v3_sql.SQLTx(tx), - database.And( - domainRepo.InstanceIDCondition(e.Aggregate().InstanceID), - domainRepo.OrgIDCondition(e.Aggregate().ResourceOwner), - domainRepo.DomainCondition(database.TextOperationEqual, e.Domain), - ), + domainRepo.PrimaryKeyCondition(e.Aggregate().InstanceID, e.Aggregate().ResourceOwner, e.Domain), domainRepo.SetVerified(), domainRepo.SetUpdatedAt(e.CreationDate()), )