Skip to content

feat(store): user delete keeps the row - #2023

Merged
AmanGIT07 merged 2 commits into
mainfrom
feat/user-delete-keeps-the-row
Oct 27, 2026
Merged

AmanGIT07 merged 2 commits into
mainfrom
feat/user-delete-keeps-the-row

Conversation

@AmanGIT07

@AmanGIT07 AmanGIT07 commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Deleting a user now keeps the user row and sets deleted_at, like the other soft deletes. The user.deleted audit record takes its time from deleted_at. The organization PAT search now lists a PAT only when its owner is live.

Changes

  • internal/store/postgres/user_repository.go: Delete sets deleted_at on the live row instead of deleting it. It still returns the row and writes the user.deleted record in the same transaction, now with occurred_at equal to deleted_at. A second delete answers user.ErrNotExist.
  • internal/store/postgres/org_pats_repository.go: the main query inner joins users on the PAT owner with users.deleted_at IS NULL, instead of a left join. The count and the list both skip a PAT whose owner is deleted.

Technical Details

  • Every read in the user repository already skips deleted rows, so a deleted user answers not found everywhere, as before.
  • The email and name unique indexes cover live rows only, so a new account can use a deleted user's email and name.
  • The organization PAT search was the only query that joined users without the live filter. user_pats.user_id is NOT NULL and references users, so every PAT has an owner row, and the inner join drops exactly the PATs whose owner is deleted. The user delete removes the user's PATs before the user row, so this only matters for a PAT created while a delete runs.
  • sessions.user_id references users with ON DELETE CASCADE, so the hard delete removed the user's sessions with the row. The soft delete does not. The user delete ends the sessions just before the row (fix(user): user delete ends the user's sessions #2024), so a deleted user's session cookie still gets unauthenticated.

Test Plan

  • internal/store/postgres/user_repository_test.go TestDeleteKeepsTheRow: the row stays with deleted_at set, GetByID answers not found, a second delete answers user.ErrNotExist and writes no second record, the record's occurred_at equals deleted_at, and a new user can be created with the same email and name. With the hard delete back, the row check fails; with time.Now() back, the time check fails.
  • internal/store/postgres/org_pats_repository_test.go: the count and data queries contain the inner join with the live filter.
  • internal/store/postgres/org_pats_repository_pg_test.go: with one live and one soft-deleted owner, the list and the count return only the live owner's PAT, and a search for the deleted owner's name finds nothing. Both cases fail with the left join this PR replaces. With the live filter in the condition of a left join, the list case fails.
  • e2e TestUserAPI case 13 passes with this PR on main: after an admin deletes a logged-in user, the same session cookie gets unauthenticated.
  • go test ./internal/store/postgres/... passes.
  • go test ./test/e2e/... passes.
  • golangci-lint run ./internal/store/postgres/... reports no issues.

SQL Safety

  • Values flow through goqu expressions: softDelete with goqu.Ex{"id": id}, and the join condition built with goqu.On, goqu.I and the live helper.
  • ToSQL() callers capture and forward params (query, params, err := stmt.ToSQL(); db.…Context(ctx, …, query, params...)). Never query, _, err := ….
  • No ? placeholders inside single-quoted SQL literals in goqu.L.
  • No new //nolint:forbidigo or // #nosec G20x annotations.

@vercel

vercel Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
frontier Ready Ready Preview Oct 27, 2026 11:56am UTC

@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 46 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between dc6c8a8 and a9ace05.

Files selected for processing: 5
  • internal/store/postgres/org_pats_repository.go
  • internal/store/postgres/org_pats_repository_pg_test.go
  • internal/store/postgres/org_pats_repository_test.go
  • internal/store/postgres/user_repository.go
  • internal/store/postgres/user_repository_test.go

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between dc6c8a8 and dc6c8a8.

Files selected for processing: 4
  • core/deleter/mocks/membership_service.go
  • core/deleter/service.go
  • core/deleter/service_test.go
  • test/e2e/regression/api_test.go

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

📝 Summary

Summary by CodeRabbit

  • Bug Fixes
    • Organization PAT searches now exclude tokens belonging to deleted users, and result totals reflect only tokens owned by active users.
    • Deleting a user preserves the record as deleted rather than removing it permanently. Deleted users remain unavailable through lookup and deletion operations, and their name and email can be used to create a new user with a distinct identity.
    • User deletion now removes the user’s access policies, revoking access to resources granted directly or through memberships.

User deletion now removes principal policies before deleting PATs and soft-deleting the user. The user row remains with a deletion timestamp. Organization PAT searches exclude PATs owned by soft-deleted users from results and counts.

Changes: User deletion and PAT search

Layer / File(s) Summary
Remove user policies during deletion
core/deleter/service.go, core/deleter/service_test.go, core/deleter/mocks/membership_service.go, test/e2e/regression/api_test.go
DeleteUser removes organization memberships, then principal policies, then PATs, and finally the user. Errors during membership or policy removal stop later cleanup. The regression test checks that deletion removes live policies and access.
User soft deletion and audit timestamp
internal/store/postgres/user_repository.go, internal/store/postgres/user_repository_test.go
Delete now uses soft deletion and records the row’s DeletedAt.Time. Tests verify row retention, later lookup and deletion behavior, and recreation with the same name and email under a different ID.
Filter PATs by live owner
internal/store/postgres/org_pats_repository.go, internal/store/postgres/org_pats_repository_test.go, internal/store/postgres/org_pats_repository_pg_test.go
The shared PAT query joins only live users. Unit and PostgreSQL tests check that searches exclude PATs of soft-deleted users and return matching totals.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Possibly related PRs

  • raystack/frontier#2024: Ends a deleted user’s sessions before deletion. This is related to the retained user row and session cleanup.
  • raystack/frontier#2022: Adds principal-policy removal to the user deletion cascade, which this PR also uses.

Suggested reviewers: rohilsurana, whoabhisheksah

Merge Risk: Minimal · previous review dc6c8

User deletion now also removes the user's policies before cleaning up PATs and deleting the user. A failure partway through can be recovered by retrying the deletion. No merge-blocking issue remains in the reviewed changes.

Pre-merge checks: Passed 2
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coveralls

coveralls commented Oct 25, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 40499812944

Coverage increased (+0.03%) to 55.78%

Details

  • Coverage increased (+0.03%) from the base build.
  • Patch coverage: 4 of 4 lines across 2 files are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 41943
Covered Lines: 23396
Line Coverage: 55.78%
Coverage Strength: 19.57 hits per line

💛 - Coveralls

Base automatically changed from fix/user-delete-removes-every-policy to main October 27, 2026 11:48
The user row gets deleted_at instead of being removed, and the user.deleted audit record takes its occurred_at from deleted_at. A second delete answers not found.
The search inner joins users with the live filter instead of a left join, so the list and the count skip a PAT whose owner is soft-deleted.
@AmanGIT07
AmanGIT07 force-pushed the feat/user-delete-keeps-the-row branch from dc6c8a8 to a9ace05 Compare October 27, 2026 11:55
@AmanGIT07
AmanGIT07 merged commit 038bd8e into main Oct 27, 2026
8 checks passed
@AmanGIT07
AmanGIT07 deleted the feat/user-delete-keeps-the-row branch October 27, 2026 12:01

This branch was successfully deployed

1 active deployment
Preview — a9ace05c Deployed Oct 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants