fixup! fixup! feat(db): Adding identity_providers table for relational database

This commit is contained in:
Iraq Jaber
2025-07-18 15:55:18 +01:00
parent d951a8b13e
commit 4638118e29
7 changed files with 403 additions and 35 deletions

View File

@@ -22,18 +22,18 @@ func IDProviderRepository(client database.QueryExecutor) domain.IDProviderReposi
}
}
const queryIDProviderStmt = `instance_id, org_id, id, state, name, type, allow_creation, allow_auto_creation,` +
const queryIDProviderStmt = `SELECT instance_id, org_id, id, state, name, type, allow_creation, allow_auto_creation,` +
` allow_auto_update, allow_linking, styling_type, payload, created_at, updated_at` +
` FROM zitadel.identity_providers`
func (i *idProvider) Get(ctx context.Context, id string) (*domain.IdentityProvider, error) {
func (i *idProvider) Get(ctx context.Context, id domain.IDPIdentifierCondition, instnaceID string, orgID string) (*domain.IdentityProvider, error) {
builder := database.StatementBuilder{}
builder.WriteString(queryIDProviderStmt)
idCondition := i.IDCondition(id)
conditions := []database.Condition{id, i.InstanceIDCondition(instnaceID), i.OrgIDCondition(orgID)}
writeCondition(&builder, idCondition)
writeCondition(&builder, database.And(conditions...))
return scanIDProvider(ctx, i.client, &builder)
}
@@ -56,13 +56,13 @@ func (i *idProvider) List(ctx context.Context, conditions ...database.Condition)
const createIDProviderStmt = `INSERT INTO zitadel.identity_providers` +
` (instance_id, org_id, id, state, name, type, allow_creation, allow_auto_creation,` +
` allow_auto_update, allow_linking, styling_type, payload)` +
` VALUES ($1, $2, $3, $4, $5, $6, $,7, $8, $9, $10, $11, $12)` +
` VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)` +
` RETURNING created_at, updated_at`
func (i *idProvider) Create(ctx context.Context, idp *domain.IdentityProvider) error {
builder := database.StatementBuilder{}
builder.AppendArgs(idp.InstanceID, idp.OrgID, idp.ID, idp.State, idp.Name, idp.Type, idp.AllowCreation,
idp.AllowAutoCreation, idp.AllowLinking, idp.StylingType, idp.Payload)
idp.AllowAutoCreation, idp.AllowAutoUpdate, idp.AllowLinking, idp.StylingType, idp.Payload)
builder.WriteString(createIDProviderStmt)
err := i.client.QueryRow(ctx, builder.String(), builder.Args()...).Scan(&idp.CreatedAt, &idp.UpdatedAt)
@@ -72,14 +72,15 @@ func (i *idProvider) Create(ctx context.Context, idp *domain.IdentityProvider) e
return nil
}
func (i *idProvider) Update(ctx context.Context, id string, changes ...database.Change) (int64, error) {
func (i *idProvider) Update(ctx context.Context, id domain.IDPIdentifierCondition, changes ...database.Change) (int64, error) {
if changes == nil {
return 0, errors.New("Update must contain a condition") // (otherwise ALL identity_providers will be updated)
}
builder := database.StatementBuilder{}
builder.WriteString(`UPDATE zitadel.identity_provider SET `)
conditions := []database.Condition{i.IDCondition(id)}
// conditions := []database.Condition{i.IDCondition(id)}
conditions := []database.Condition{id}
database.Changes(changes).Write(&builder)
writeCondition(&builder, database.And(conditions...))
@@ -89,13 +90,13 @@ func (i *idProvider) Update(ctx context.Context, id string, changes ...database.
return rowsAffected, err
}
func (i *idProvider) Delete(ctx context.Context, id string) (int64, error) {
func (i *idProvider) Delete(ctx context.Context, id domain.IDPIdentifierCondition) (int64, error) {
builder := database.StatementBuilder{}
builder.WriteString(`DELETE FROM zitadel.identity_providers`)
conditions := []database.Condition{i.IDCondition(id)}
writeCondition(&builder, database.And(conditions...))
// conditions := []database.Condition{i.IDCondition(id)}
// writeCondition(&builder, database.And(conditions...))
return i.client.Exec(ctx, builder.String(), builder.Args()...)
}
@@ -172,7 +173,7 @@ func (i idProvider) OrgIDCondition(id string) database.Condition {
return database.NewTextCondition(i.OrgIDColumn(), database.TextOperationEqual, id)
}
func (i idProvider) IDCondition(id string) database.Condition {
func (i idProvider) IDCondition(id string) domain.IDPIdentifierCondition {
return database.NewTextCondition(i.IDColumn(), database.TextOperationEqual, id)
}
@@ -180,7 +181,7 @@ func (i idProvider) StateCondition(state domain.IDPState) database.Condition {
return database.NewTextCondition(i.OrgIDColumn(), database.TextOperationEqual, state.String())
}
func (i idProvider) NameCondition(name string) database.Condition {
func (i idProvider) NameCondition(name string) domain.IDPIdentifierCondition {
return database.NewTextCondition(i.NameColumn(), database.TextOperationEqual, name)
}

View File

@@ -0,0 +1,345 @@
package repository_test
import (
"context"
"testing"
"time"
"github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zitadel/zitadel/backend/v3/domain"
"github.com/zitadel/zitadel/backend/v3/storage/database"
"github.com/zitadel/zitadel/backend/v3/storage/database/repository"
)
func TestCreateIDProvider(t *testing.T) {
// create instance
instanceId := gofakeit.Name()
instance := domain.Instance{
ID: instanceId,
Name: gofakeit.Name(),
DefaultOrgID: "defaultOrgId",
IAMProjectID: "iamProject",
ConsoleClientID: "consoleCLient",
ConsoleAppID: "consoleApp",
DefaultLanguage: "defaultLanguage",
}
instanceRepo := repository.InstanceRepository(pool)
err := instanceRepo.Create(t.Context(), &instance)
require.NoError(t, err)
// create org
orgId := gofakeit.Name()
org := domain.Organization{
ID: orgId,
Name: gofakeit.Name(),
InstanceID: instanceId,
State: domain.OrgStateActive.String(),
}
organizationRepo := repository.OrganizationRepository(pool)
err = organizationRepo.Create(t.Context(), &org)
require.NoError(t, err)
type test struct {
name string
testFunc func(ctx context.Context, t *testing.T) *domain.IdentityProvider
idp domain.IdentityProvider
err error
}
// TESTS
tests := []test{
{
name: "happy path",
idp: domain.IdentityProvider{
InstanceID: instanceId,
OrgID: orgId,
ID: gofakeit.Name(),
State: domain.IDPStateActive.String(),
Name: gofakeit.Name(),
Type: domain.IDPTypeOIDC.String(),
AllowCreation: true,
AllowAutoCreation: true,
AllowAutoUpdate: true,
AllowLinking: true,
StylingType: 1,
Payload: "{}",
},
},
{
name: "create organization without name",
idp: domain.IdentityProvider{
InstanceID: instanceId,
OrgID: orgId,
ID: gofakeit.Name(),
State: domain.IDPStateActive.String(),
// Name: gofakeit.Name(),
Type: domain.IDPTypeOIDC.String(),
AllowCreation: true,
AllowAutoCreation: true,
AllowAutoUpdate: true,
AllowLinking: true,
StylingType: 1,
Payload: "{}",
},
err: new(database.CheckError),
},
{
name: "adding org with same id twice",
testFunc: func(ctx context.Context, t *testing.T) *domain.IdentityProvider {
idpRepo := repository.IDProviderRepository(pool)
idp := domain.IdentityProvider{
InstanceID: instanceId,
OrgID: orgId,
ID: gofakeit.Name(),
State: domain.IDPStateActive.String(),
Name: gofakeit.Name(),
Type: domain.IDPTypeOIDC.String(),
AllowCreation: true,
AllowAutoCreation: true,
AllowAutoUpdate: true,
AllowLinking: true,
StylingType: 1,
Payload: "{}",
}
err := idpRepo.Create(ctx, &idp)
require.NoError(t, err)
// change the name to make sure same only the id clashes
org.Name = gofakeit.Name()
return &idp
},
err: new(database.UniqueError),
},
{
name: "adding org with same name twice",
testFunc: func(ctx context.Context, t *testing.T) *domain.IdentityProvider {
idpRepo := repository.IDProviderRepository(pool)
idp := domain.IdentityProvider{
InstanceID: instanceId,
OrgID: orgId,
ID: gofakeit.Name(),
State: domain.IDPStateActive.String(),
Name: gofakeit.Name(),
Type: domain.IDPTypeOIDC.String(),
AllowCreation: true,
AllowAutoCreation: true,
AllowAutoUpdate: true,
AllowLinking: true,
StylingType: 1,
Payload: "{}",
}
err := idpRepo.Create(ctx, &idp)
require.NoError(t, err)
// change the id to make sure same name causes an error
idp.ID = gofakeit.Name()
return &idp
},
err: new(database.UniqueError),
},
func() test {
id := gofakeit.Name()
name := gofakeit.Name()
return test{
name: "adding org with same name, id, different instance",
testFunc: func(ctx context.Context, t *testing.T) *domain.IdentityProvider {
// create instance
newInstId := gofakeit.Name()
instance := domain.Instance{
ID: newInstId,
Name: gofakeit.Name(),
DefaultOrgID: "defaultOrgId",
IAMProjectID: "iamProject",
ConsoleClientID: "consoleCLient",
ConsoleAppID: "consoleApp",
DefaultLanguage: "defaultLanguage",
}
instanceRepo := repository.InstanceRepository(pool)
err := instanceRepo.Create(ctx, &instance)
assert.Nil(t, err)
// create org
newOrgId := gofakeit.Name()
org := domain.Organization{
ID: newOrgId,
Name: gofakeit.Name(),
InstanceID: newInstId,
State: domain.OrgStateActive.String(),
}
organizationRepo := repository.OrganizationRepository(pool)
err = organizationRepo.Create(t.Context(), &org)
require.NoError(t, err)
idpRepo := repository.IDProviderRepository(pool)
idp := domain.IdentityProvider{
InstanceID: newInstId,
OrgID: newOrgId,
ID: id,
State: domain.IDPStateActive.String(),
Name: name,
Type: domain.IDPTypeOIDC.String(),
AllowCreation: true,
AllowAutoCreation: true,
AllowAutoUpdate: true,
AllowLinking: true,
StylingType: 1,
Payload: "{}",
}
err = idpRepo.Create(ctx, &idp)
require.NoError(t, err)
// change the instanceID to a different instance
idp.InstanceID = instanceId
// change the OrgId to a different organization
idp.OrgID = orgId
return &idp
},
idp: domain.IdentityProvider{
InstanceID: instanceId,
OrgID: orgId,
ID: id,
State: domain.IDPStateActive.String(),
Name: name,
Type: domain.IDPTypeOIDC.String(),
AllowCreation: true,
AllowAutoCreation: true,
AllowAutoUpdate: true,
AllowLinking: true,
StylingType: 1,
Payload: "{}",
},
}
}(),
{
name: "adding idp with no id",
idp: domain.IdentityProvider{
InstanceID: instanceId,
OrgID: orgId,
// ID: gofakeit.Name(),
State: domain.IDPStateActive.String(),
Name: gofakeit.Name(),
Type: domain.IDPTypeOIDC.String(),
AllowCreation: true,
AllowAutoCreation: true,
AllowAutoUpdate: true,
AllowLinking: true,
StylingType: 1,
Payload: "{}",
},
err: new(database.CheckError),
},
{
name: "adding idp with no name",
idp: domain.IdentityProvider{
InstanceID: instanceId,
OrgID: orgId,
ID: gofakeit.Name(),
State: domain.IDPStateActive.String(),
// Name: gofakeit.Name(),
Type: domain.IDPTypeOIDC.String(),
AllowCreation: true,
AllowAutoCreation: true,
AllowAutoUpdate: true,
AllowLinking: true,
StylingType: 1,
Payload: "{}",
},
err: new(database.CheckError),
},
{
name: "adding idp with no instance id",
idp: domain.IdentityProvider{
// InstanceID: instanceId,
OrgID: orgId,
State: domain.IDPStateActive.String(),
Name: gofakeit.Name(),
Type: domain.IDPTypeOIDC.String(),
AllowCreation: true,
AllowAutoCreation: true,
AllowAutoUpdate: true,
AllowLinking: true,
StylingType: 1,
Payload: "{}",
},
err: new(database.IntegrityViolationError),
},
{
name: "adding organization with non existent instance id",
idp: domain.IdentityProvider{
InstanceID: gofakeit.Name(),
OrgID: orgId,
State: domain.IDPStateActive.String(),
ID: gofakeit.Name(),
Name: gofakeit.Name(),
Type: domain.IDPTypeOIDC.String(),
AllowCreation: true,
AllowAutoCreation: true,
AllowAutoUpdate: true,
AllowLinking: true,
StylingType: 1,
Payload: "{}",
},
err: new(database.ForeignKeyError),
},
{
name: "adding organization with non existent org id",
idp: domain.IdentityProvider{
InstanceID: instanceId,
OrgID: gofakeit.Name(),
State: domain.IDPStateActive.String(),
ID: gofakeit.Name(),
Name: gofakeit.Name(),
Type: domain.IDPTypeOIDC.String(),
AllowCreation: true,
AllowAutoCreation: true,
AllowAutoUpdate: true,
AllowLinking: true,
StylingType: 1,
Payload: "{}",
},
err: new(database.ForeignKeyError),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
var idp *domain.IdentityProvider
if tt.testFunc != nil {
idp = tt.testFunc(ctx, t)
} else {
idp = &tt.idp
}
idpRepo := repository.IDProviderRepository(pool)
// create idp
beforeCreate := time.Now()
err = idpRepo.Create(ctx, idp)
assert.ErrorIs(t, err, tt.err)
if err != nil {
return
}
afterCreate := time.Now()
// check organization values
idp, err = idpRepo.Get(ctx,
idpRepo.IDCondition(idp.ID),
idp.InstanceID,
idp.OrgID,
)
require.NoError(t, err)
assert.Equal(t, tt.idp.ID, idp.ID)
assert.Equal(t, tt.idp.Name, idp.Name)
assert.Equal(t, tt.idp.InstanceID, idp.InstanceID)
assert.Equal(t, tt.idp.State, idp.State)
assert.WithinRange(t, idp.CreatedAt, beforeCreate, afterCreate)
assert.WithinRange(t, idp.UpdatedAt, beforeCreate, afterCreate)
})
}
}