From 80b811c12e2934f0611fa3f210d3d205c0804569 Mon Sep 17 00:00:00 2001 From: Silvan Date: Tue, 2 Nov 2021 11:00:48 +0100 Subject: [PATCH] fix(projection): add idp login policy link (#2590) * fix(projections): add app * fix(migration): add index for project_id * test: app projection * fix(projections): add idp_user_link * test: idp user link * fix(projection): add idp login policy link * fix: migration versions * fix: migration versions * refactor: rename externalIDP to UserIDPLink * fix: interface * fix: interface methods --- .../query/projection/idp_login_policy_link.go | 148 +++++++++++ .../projection/idp_login_policy_link_test.go | 235 ++++++++++++++++++ internal/query/projection/projection.go | 1 + .../V1.89__idp_login_policy_link.sql | 12 + 4 files changed, 396 insertions(+) create mode 100644 internal/query/projection/idp_login_policy_link.go create mode 100644 internal/query/projection/idp_login_policy_link_test.go create mode 100644 migrations/cockroach/V1.89__idp_login_policy_link.sql diff --git a/internal/query/projection/idp_login_policy_link.go b/internal/query/projection/idp_login_policy_link.go new file mode 100644 index 0000000000..33503fb180 --- /dev/null +++ b/internal/query/projection/idp_login_policy_link.go @@ -0,0 +1,148 @@ +package projection + +import ( + "context" + + "github.com/caos/logging" + "github.com/caos/zitadel/internal/domain" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/handler" + "github.com/caos/zitadel/internal/eventstore/handler/crdb" + "github.com/caos/zitadel/internal/repository/iam" + "github.com/caos/zitadel/internal/repository/org" + "github.com/caos/zitadel/internal/repository/policy" +) + +type IDPLoginPolicyLinkProjection struct { + crdb.StatementHandler +} + +const ( + IDPLoginPolicyLinkTable = "zitadel.projections.idp_login_policy_links" +) + +func NewIDPLoginPolicyLinkProjection(ctx context.Context, config crdb.StatementHandlerConfig) *IDPLoginPolicyLinkProjection { + p := &IDPLoginPolicyLinkProjection{} + config.ProjectionName = IDPLoginPolicyLinkTable + config.Reducers = p.reducers() + p.StatementHandler = crdb.NewStatementHandler(ctx, config) + return p +} + +func (p *IDPLoginPolicyLinkProjection) reducers() []handler.AggregateReducer { + return []handler.AggregateReducer{ + { + Aggregate: org.AggregateType, + EventRedusers: []handler.EventReducer{ + { + Event: org.LoginPolicyIDPProviderAddedEventType, + Reduce: p.reduceAdded, + }, + { + Event: org.LoginPolicyIDPProviderCascadeRemovedEventType, + Reduce: p.reduceCascadeRemoved, + }, + { + Event: org.LoginPolicyIDPProviderRemovedEventType, + Reduce: p.reduceRemoved, + }, + }, + }, + { + Aggregate: iam.AggregateType, + EventRedusers: []handler.EventReducer{ + { + Event: iam.LoginPolicyIDPProviderAddedEventType, + Reduce: p.reduceAdded, + }, + { + Event: iam.LoginPolicyIDPProviderCascadeRemovedEventType, + Reduce: p.reduceCascadeRemoved, + }, + { + Event: iam.LoginPolicyIDPProviderRemovedEventType, + Reduce: p.reduceRemoved, + }, + }, + }, + } +} + +const ( + IDPLoginPolicyLinkIDPIDCol = "idp_id" + IDPLoginPolicyLinkAggregateIDCol = "aggregate_id" + IDPLoginPolicyLinkCreationDateCol = "creation_date" + IDPLoginPolicyLinkChangeDateCol = "change_date" + IDPLoginPolicyLinkSequenceCol = "sequence" + IDPLoginPolicyLinkResourceOwnerCol = "resource_owner" + IDPLoginPolicyLinkProviderTypeCol = "provider_type" +) + +func (p *IDPLoginPolicyLinkProjection) reduceAdded(event eventstore.EventReader) (*handler.Statement, error) { + var ( + idp policy.IdentityProviderAddedEvent + providerType domain.IdentityProviderType + ) + + switch e := event.(type) { + case *org.IdentityProviderAddedEvent: + idp = e.IdentityProviderAddedEvent + providerType = domain.IdentityProviderTypeOrg + case *iam.IdentityProviderAddedEvent: + idp = e.IdentityProviderAddedEvent + providerType = domain.IdentityProviderTypeSystem + default: + logging.LogWithFields("HANDL-oce92", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LoginPolicyIDPProviderAddedEventType, iam.LoginPolicyIDPProviderAddedEventType}).Error("wrong event type") + return nil, errors.ThrowInvalidArgument(nil, "HANDL-Nlp55", "reduce.wrong.event.type") + } + return crdb.NewCreateStatement(&idp, + []handler.Column{ + handler.NewCol(IDPLoginPolicyLinkIDPIDCol, idp.IDPConfigID), + handler.NewCol(IDPLoginPolicyLinkAggregateIDCol, idp.Aggregate().ID), + handler.NewCol(IDPLoginPolicyLinkCreationDateCol, idp.CreationDate()), + handler.NewCol(IDPLoginPolicyLinkChangeDateCol, idp.CreationDate()), + handler.NewCol(IDPLoginPolicyLinkSequenceCol, idp.Sequence()), + handler.NewCol(IDPLoginPolicyLinkResourceOwnerCol, idp.Aggregate().ResourceOwner), + handler.NewCol(IDPLoginPolicyLinkProviderTypeCol, providerType), + }, + ), nil +} + +func (p *IDPLoginPolicyLinkProjection) reduceRemoved(event eventstore.EventReader) (*handler.Statement, error) { + var idp policy.IdentityProviderRemovedEvent + switch e := event.(type) { + case *org.IdentityProviderRemovedEvent: + idp = e.IdentityProviderRemovedEvent + case *iam.IdentityProviderRemovedEvent: + idp = e.IdentityProviderRemovedEvent + default: + logging.LogWithFields("HANDL-vAH3I", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LoginPolicyIDPProviderRemovedEventType, iam.LoginPolicyIDPProviderRemovedEventType}).Error("wrong event type") + return nil, errors.ThrowInvalidArgument(nil, "HANDL-tUMYY", "reduce.wrong.event.type") + } + return crdb.NewDeleteStatement(&idp, + []handler.Condition{ + handler.NewCond(IDPLoginPolicyLinkIDPIDCol, idp.IDPConfigID), + handler.NewCond(IDPLoginPolicyLinkAggregateIDCol, idp.Aggregate().ID), + }, + ), nil +} + +func (p *IDPLoginPolicyLinkProjection) reduceCascadeRemoved(event eventstore.EventReader) (*handler.Statement, error) { + var idp policy.IdentityProviderCascadeRemovedEvent + switch e := event.(type) { + case *org.IdentityProviderCascadeRemovedEvent: + idp = e.IdentityProviderCascadeRemovedEvent + case *iam.IdentityProviderCascadeRemovedEvent: + idp = e.IdentityProviderCascadeRemovedEvent + default: + logging.LogWithFields("HANDL-7lZaf", "seq", event.Sequence(), "expectedTypes", []eventstore.EventType{org.LoginPolicyIDPProviderCascadeRemovedEventType, iam.LoginPolicyIDPProviderCascadeRemovedEventType}).Error("wrong event type") + return nil, errors.ThrowInvalidArgument(nil, "HANDL-iCKSj", "reduce.wrong.event.type") + } + return crdb.NewDeleteStatement(&idp, + []handler.Condition{ + handler.NewCond(IDPLoginPolicyLinkIDPIDCol, idp.IDPConfigID), + handler.NewCond(IDPLoginPolicyLinkAggregateIDCol, idp.Aggregate().ID), + }, + ), nil +} diff --git a/internal/query/projection/idp_login_policy_link_test.go b/internal/query/projection/idp_login_policy_link_test.go new file mode 100644 index 0000000000..036042cfb4 --- /dev/null +++ b/internal/query/projection/idp_login_policy_link_test.go @@ -0,0 +1,235 @@ +package projection + +import ( + "testing" + + "github.com/caos/zitadel/internal/domain" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore" + "github.com/caos/zitadel/internal/eventstore/handler" + "github.com/caos/zitadel/internal/eventstore/repository" + "github.com/caos/zitadel/internal/repository/iam" + "github.com/caos/zitadel/internal/repository/org" +) + +func TestIDPLoginPolicyLinkProjection_reduces(t *testing.T) { + type args struct { + event func(t *testing.T) eventstore.EventReader + } + tests := []struct { + name string + args args + reduce func(event eventstore.EventReader) (*handler.Statement, error) + want wantReduce + }{ + { + name: "iam.reduceAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LoginPolicyIDPProviderAddedEventType), + iam.AggregateType, + []byte(`{ + "idpConfigId": "idp-config-id", + "idpProviderType": 1 +}`), + ), iam.IdentityProviderAddedEventMapper), + }, + reduce: (&IDPLoginPolicyLinkProjection{}).reduceAdded, + want: wantReduce{ + aggregateType: iam.AggregateType, + sequence: 15, + previousSequence: 10, + projection: IDPLoginPolicyLinkTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "INSERT INTO zitadel.projections.idp_login_policy_links (idp_id, aggregate_id, creation_date, change_date, sequence, resource_owner, provider_type) VALUES ($1, $2, $3, $4, $5, $6, $7)", + expectedArgs: []interface{}{ + "idp-config-id", + "agg-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + domain.IdentityProviderTypeSystem, + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceRemoved", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LoginPolicyIDPProviderRemovedEventType), + iam.AggregateType, + []byte(`{ + "idpConfigId": "idp-config-id", + "idpProviderType": 1 +}`), + ), iam.IdentityProviderRemovedEventMapper), + }, + reduce: (&IDPLoginPolicyLinkProjection{}).reduceRemoved, + want: wantReduce{ + aggregateType: iam.AggregateType, + sequence: 15, + previousSequence: 10, + projection: IDPLoginPolicyLinkTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "DELETE FROM zitadel.projections.idp_login_policy_links WHERE (idp_id = $1) AND (aggregate_id = $2)", + expectedArgs: []interface{}{ + "idp-config-id", + "agg-id", + }, + }, + }, + }, + }, + }, + { + name: "iam.reduceCascadeRemoved", + args: args{ + event: getEvent(testEvent( + repository.EventType(iam.LoginPolicyIDPProviderCascadeRemovedEventType), + iam.AggregateType, + []byte(`{ + "idpConfigId": "idp-config-id", + "idpProviderType": 1 +}`), + ), iam.IdentityProviderCascadeRemovedEventMapper), + }, + reduce: (&IDPLoginPolicyLinkProjection{}).reduceCascadeRemoved, + want: wantReduce{ + aggregateType: iam.AggregateType, + sequence: 15, + previousSequence: 10, + projection: IDPLoginPolicyLinkTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "DELETE FROM zitadel.projections.idp_login_policy_links WHERE (idp_id = $1) AND (aggregate_id = $2)", + expectedArgs: []interface{}{ + "idp-config-id", + "agg-id", + }, + }, + }, + }, + }, + }, + { + name: "org.reduceAdded", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LoginPolicyIDPProviderAddedEventType), + org.AggregateType, + []byte(`{ + "idpConfigId": "idp-config-id", + "idpProviderType": 1 +}`), + ), org.IdentityProviderAddedEventMapper), + }, + reduce: (&IDPLoginPolicyLinkProjection{}).reduceAdded, + want: wantReduce{ + aggregateType: org.AggregateType, + sequence: 15, + previousSequence: 10, + projection: IDPLoginPolicyLinkTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "INSERT INTO zitadel.projections.idp_login_policy_links (idp_id, aggregate_id, creation_date, change_date, sequence, resource_owner, provider_type) VALUES ($1, $2, $3, $4, $5, $6, $7)", + expectedArgs: []interface{}{ + "idp-config-id", + "agg-id", + anyArg{}, + anyArg{}, + uint64(15), + "ro-id", + domain.IdentityProviderTypeOrg, + }, + }, + }, + }, + }, + }, + { + name: "org.reduceRemoved", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LoginPolicyIDPProviderRemovedEventType), + org.AggregateType, + []byte(`{ + "idpConfigId": "idp-config-id", + "idpProviderType": 1 +}`), + ), org.IdentityProviderRemovedEventMapper), + }, + reduce: (&IDPLoginPolicyLinkProjection{}).reduceRemoved, + want: wantReduce{ + aggregateType: org.AggregateType, + sequence: 15, + previousSequence: 10, + projection: IDPLoginPolicyLinkTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "DELETE FROM zitadel.projections.idp_login_policy_links WHERE (idp_id = $1) AND (aggregate_id = $2)", + expectedArgs: []interface{}{ + "idp-config-id", + "agg-id", + }, + }, + }, + }, + }, + }, + { + name: "org.reduceCascadeRemoved", + args: args{ + event: getEvent(testEvent( + repository.EventType(org.LoginPolicyIDPProviderCascadeRemovedEventType), + org.AggregateType, + []byte(`{ + "idpConfigId": "idp-config-id", + "idpProviderType": 1 +}`), + ), org.IdentityProviderCascadeRemovedEventMapper), + }, + reduce: (&IDPLoginPolicyLinkProjection{}).reduceCascadeRemoved, + want: wantReduce{ + aggregateType: org.AggregateType, + sequence: 15, + previousSequence: 10, + projection: IDPLoginPolicyLinkTable, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "DELETE FROM zitadel.projections.idp_login_policy_links WHERE (idp_id = $1) AND (aggregate_id = $2)", + expectedArgs: []interface{}{ + "idp-config-id", + "agg-id", + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + event := baseEvent(t) + got, err := tt.reduce(event) + if _, ok := err.(errors.InvalidArgument); !ok { + t.Errorf("no wrong event mapping: %v, got: %v", err, got) + } + + event = tt.args.event(t) + got, err = tt.reduce(event) + assertReduce(t, got, err, tt.want) + }) + } +} diff --git a/internal/query/projection/projection.go b/internal/query/projection/projection.go index 426f4fbe8e..3318e60da7 100644 --- a/internal/query/projection/projection.go +++ b/internal/query/projection/projection.go @@ -50,6 +50,7 @@ func Start(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, co NewIDPProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["idps"])) NewAppProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["apps"])) NewIDPUserLinkProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["idp_user_links"])) + NewIDPLoginPolicyLinkProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["idp_login_policy_links"])) NewMailTemplateProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["mail_templates"])) NewMessageTextProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["message_texts"])) NewCustomTextProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["custom_texts"])) diff --git a/migrations/cockroach/V1.89__idp_login_policy_link.sql b/migrations/cockroach/V1.89__idp_login_policy_link.sql new file mode 100644 index 0000000000..9278ad1613 --- /dev/null +++ b/migrations/cockroach/V1.89__idp_login_policy_link.sql @@ -0,0 +1,12 @@ +CREATE TABLE zitadel.projections.idp_login_policy_links( + idp_id STRING, + aggregate_id STRING, + provider_type INT2, + + creation_date TIMESTAMPTZ, + change_date TIMESTAMPTZ, + sequence INT8, + resource_owner STRING, + + PRIMARY KEY (aggregate_id, idp_id) +);