From 71472189cf78417fe5b84ffc4f79b03dfcb5224a Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Mon, 12 May 2025 12:05:12 +0200 Subject: [PATCH] fix(projection): remove users with factors (#9877) # Which Problems Are Solved When users are removed, their auth factors stay in the projection. This data inconsistency is visible if a removed user is recreated with the same ID. In such a case, the login UI and the query API methods show the removed users auth methods. This is unexpected behavior. The old users auth methods are not usable to log in and they are not found by the command side. This is expected behavior. # How the Problems Are Solved The auth factors projection reduces the user removed event by deleting all factors. # Additional Context - Reported by support request - requires backport to 2.x and 3.x (cherry picked from commit d79d5e7b964e3f1592489e956156f8f1c87e4710) --- internal/query/projection/user_auth_method.go | 19 +++++++++++++ .../query/projection/user_auth_method_test.go | 28 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/internal/query/projection/user_auth_method.go b/internal/query/projection/user_auth_method.go index b986df1558..7726550ffd 100644 --- a/internal/query/projection/user_auth_method.go +++ b/internal/query/projection/user_auth_method.go @@ -125,6 +125,10 @@ func (p *userAuthMethodProjection) Reducers() []handler.AggregateReducer { Event: user.HumanOTPEmailRemovedType, Reduce: p.reduceRemoveAuthMethod, }, + { + Event: user.UserRemovedType, + Reduce: p.reduceUserRemoved, + }, }, }, { @@ -311,3 +315,18 @@ func (p *userAuthMethodProjection) reduceOwnerRemoved(event eventstore.Event) (* }, ), nil } + +func (p *userAuthMethodProjection) reduceUserRemoved(event eventstore.Event) (*handler.Statement, error) { + e, ok := event.(*user.UserRemovedEvent) + if !ok { + return nil, zerrors.ThrowInvalidArgumentf(nil, "PROJE-FwDZ8", "reduce.wrong.event.type %s", user.UserRemovedType) + } + return handler.NewDeleteStatement( + e, + []handler.Condition{ + handler.NewCond(UserAuthMethodInstanceIDCol, e.Aggregate().InstanceID), + handler.NewCond(UserAuthMethodResourceOwnerCol, e.Aggregate().ResourceOwner), + handler.NewCond(UserAuthMethodUserIDCol, e.Aggregate().ID), + }, + ), nil +} diff --git a/internal/query/projection/user_auth_method_test.go b/internal/query/projection/user_auth_method_test.go index fb3d6d9d91..e21a480a9d 100644 --- a/internal/query/projection/user_auth_method_test.go +++ b/internal/query/projection/user_auth_method_test.go @@ -528,6 +528,34 @@ func TestUserAuthMethodProjection_reduces(t *testing.T) { }, }, }, + { + name: "reduceUserRemoved", + reduce: (&userAuthMethodProjection{}).reduceUserRemoved, + args: args{ + event: getEvent( + testEvent( + user.UserRemovedType, + user.AggregateType, + nil, + ), user.UserRemovedEventMapper), + }, + want: wantReduce{ + aggregateType: eventstore.AggregateType("user"), + sequence: 15, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "DELETE FROM projections.user_auth_methods5 WHERE (instance_id = $1) AND (resource_owner = $2) AND (user_id = $3)", + expectedArgs: []interface{}{ + "instance-id", + "ro-id", + "agg-id", + }, + }, + }, + }, + }, + }, { name: "org reduceOwnerRemoved", reduce: (&userAuthMethodProjection{}).reduceOwnerRemoved,