diff --git a/core/user/service.go b/core/user/service.go index 6763ed83f..a88bf4cb9 100644 --- a/core/user/service.go +++ b/core/user/service.go @@ -166,6 +166,9 @@ func (s Service) Delete(ctx context.Context, id string) error { }}); err != nil { return err } + if err := s.sessionService.DeleteByUserID(ctx, id); err != nil { + return err + } return s.repository.Delete(ctx, id) } diff --git a/core/user/service_test.go b/core/user/service_test.go index 7c98dd5c5..792568857 100644 --- a/core/user/service_test.go +++ b/core/user/service_test.go @@ -592,6 +592,23 @@ func TestService_Enable(t *testing.T) { } func TestService_Delete(t *testing.T) { + t.Run("removes the relations, then ends the sessions, then removes the user", func(t *testing.T) { + repo, relationService, sessionService, auditRecordRepository := mockService(t) + var steps []string + relationService.EXPECT().Delete(mock.Anything, relation.Relation{Subject: relation.Subject{ + ID: "test-id", + Namespace: schema.UserPrincipal, + }}).Run(func(context.Context, relation.Relation) { steps = append(steps, "relations") }).Return(nil) + sessionService.EXPECT().DeleteByUserID(mock.Anything, "test-id"). + Run(func(context.Context, string) { steps = append(steps, "sessions") }).Return(nil) + repo.EXPECT().Delete(mock.Anything, "test-id"). + Run(func(context.Context, string) { steps = append(steps, "user") }).Return(nil) + + err := user.NewService(repo, relationService, sessionService, auditRecordRepository).Delete(context.Background(), "test-id") + assert.NoError(t, err) + assert.Equal(t, []string{"relations", "sessions", "user"}, steps) + }) + tests := []struct { name string id string @@ -599,21 +616,20 @@ func TestService_Delete(t *testing.T) { wantErr bool }{ { - name: "while deleting user, delete it's relations", + name: "fail on delete relations shouldn't end sessions or remove user", id: "test-id", - wantErr: false, + wantErr: true, setup: func() *user.Service { repo, relationService, sessionService, auditRecordRepository := mockService(t) relationService.EXPECT().Delete(mock.Anything, relation.Relation{Subject: relation.Subject{ ID: "test-id", Namespace: schema.UserPrincipal, - }}).Return(nil) - repo.EXPECT().Delete(mock.Anything, "test-id").Return(nil) + }}).Return(errors.New("failed to delete relation")) return user.NewService(repo, relationService, sessionService, auditRecordRepository) }, }, { - name: "fail on delete relations shouldn't remove user", + name: "fail on ending sessions shouldn't remove user", id: "test-id", wantErr: true, setup: func() *user.Service { @@ -621,7 +637,8 @@ func TestService_Delete(t *testing.T) { relationService.EXPECT().Delete(mock.Anything, relation.Relation{Subject: relation.Subject{ ID: "test-id", Namespace: schema.UserPrincipal, - }}).Return(errors.New("failed to delete relation")) + }}).Return(nil) + sessionService.EXPECT().DeleteByUserID(mock.Anything, "test-id").Return(errors.New("failed to end sessions")) return user.NewService(repo, relationService, sessionService, auditRecordRepository) }, }, diff --git a/test/e2e/regression/api_test.go b/test/e2e/regression/api_test.go index 62be22653..a9a1c24ba 100644 --- a/test/e2e/regression/api_test.go +++ b/test/e2e/regression/api_test.go @@ -1721,6 +1721,36 @@ func (s *APIRegressionTestSuite) TestUserAPI() { })) s.Require().NoError(err) + _, err = s.testBench.Client.GetCurrentUser(ctxTarget, connect.NewRequest(&frontierv1beta1.GetCurrentUserRequest{})) + s.Require().Error(err) + s.Assert().Equal(connect.CodeUnauthenticated, connect.CodeOf(err)) + }) + s.Run("13. session is rejected on the next request after the user is deleted", func() { + const targetEmail = "delete-session-target@raystack.org" + + createResp, err := s.testBench.Client.CreateUser(ctxOrgAdminAuth, connect.NewRequest(&frontierv1beta1.CreateUserRequest{ + Body: &frontierv1beta1.UserRequestBody{ + Title: "delete session target", + Email: targetEmail, + Name: "delete_session_target", + }, + })) + s.Require().NoError(err) + targetID := createResp.Msg.GetUser().GetId() + + targetCookie, err := testbench.AuthenticateUser(context.Background(), s.testBench.Client, targetEmail) + s.Require().NoError(err) + ctxTarget := testbench.ContextWithAuth(context.Background(), targetCookie) + + meResp, err := s.testBench.Client.GetCurrentUser(ctxTarget, connect.NewRequest(&frontierv1beta1.GetCurrentUserRequest{})) + s.Require().NoError(err) + s.Assert().Equal(targetEmail, meResp.Msg.GetUser().GetEmail()) + + _, err = s.testBench.Client.DeleteUser(ctxOrgAdminAuth, connect.NewRequest(&frontierv1beta1.DeleteUserRequest{ + Id: targetID, + })) + s.Require().NoError(err) + _, err = s.testBench.Client.GetCurrentUser(ctxTarget, connect.NewRequest(&frontierv1beta1.GetCurrentUserRequest{})) s.Require().Error(err) s.Assert().Equal(connect.CodeUnauthenticated, connect.CodeOf(err))