fix(auth): wipe user-owned content before Better-Auth deletes the user #51

Merged
eliott merged 4 commits from audit/legal-compliance into develop 2026-05-16 21:29:46 +02:00
Owner

Summary

  • Wire DataWipe.wipeUserData into Better-Auth's user.deleteUser.beforeDelete hook so account deletion succeeds even when the user owns shoots/frames/personal globals.
  • Add an integration test that reproduces the original 23503 FK violation and asserts the new behaviour.
  • Bump to 8.1.2.
  • Add the v7.2.1 changelog entry seeded for rollnote-front 7.2.1.

Why

Audit of the legal pages found that the privacy policy promised "if you delete your account, all associated data is permanently deleted" but the actual delete-account button (authClient.deleteUser -> /api/auth/delete-user) was failing with update or delete on table "user" violates foreign key constraint "frame_createdBy_user_id_fk". The user.id FKs on shoot/frame/folder/camera/lens/filter/weather/iso/film/film_format/film do not cascade, and there was no hook wiping owned content first.

DataWipe.wipeUserData already existed (used by the import/export "wipe + reimport" flow); this PR reuses it from auth.ts via a plain new DataWipe() since the hook fires outside the Nest DI container.

Test plan

  • New test/int/auth-delete-user.int-spec.ts - RED before fix (FK violation, 500), GREEN after.
  • pnpm test:int - 101/101 passing.
  • pnpm test - 63/63 passing.
  • pnpm build - clean.
  • pnpm lint - clean.
  • Manual: sign up, create a shoot + frame, delete account from /account in the front; expect 200 and no orphan rows.
## Summary - Wire `DataWipe.wipeUserData` into Better-Auth's `user.deleteUser.beforeDelete` hook so account deletion succeeds even when the user owns shoots/frames/personal globals. - Add an integration test that reproduces the original 23503 FK violation and asserts the new behaviour. - Bump to 8.1.2. - Add the v7.2.1 changelog entry seeded for rollnote-front 7.2.1. ## Why Audit of the legal pages found that the privacy policy promised "if you delete your account, all associated data is permanently deleted" but the actual delete-account button (`authClient.deleteUser` -> `/api/auth/delete-user`) was failing with `update or delete on table "user" violates foreign key constraint "frame_createdBy_user_id_fk"`. The user.id FKs on shoot/frame/folder/camera/lens/filter/weather/iso/film/film_format/film do not cascade, and there was no hook wiping owned content first. `DataWipe.wipeUserData` already existed (used by the import/export "wipe + reimport" flow); this PR reuses it from auth.ts via a plain `new DataWipe()` since the hook fires outside the Nest DI container. ## Test plan - [x] New `test/int/auth-delete-user.int-spec.ts` - RED before fix (FK violation, 500), GREEN after. - [x] `pnpm test:int` - 101/101 passing. - [x] `pnpm test` - 63/63 passing. - [x] `pnpm build` - clean. - [x] `pnpm lint` - clean. - [ ] Manual: sign up, create a shoot + frame, delete account from `/account` in the front; expect 200 and no orphan rows.
fix(auth): wipe user-owned content before Better-Auth deletes the user
Some checks failed
tests / test (pull_request) Failing after 55s
8565946f8c
The standard delete-account flow was hitting a 23503 FK violation as
soon as a user had any shoots, frames, or personal globals - the
created_by FKs on those tables do not cascade. Wire DataWipe.wipeUserData
into the user.deleteUser.beforeDelete hook so the deletion now matches
what the privacy policy promises. Add an integration test that creates a
shoot + frame, deletes the account, and asserts everything is gone.

Bump to 8.1.2 and add a v7.2.1 changelog entry (matches rollnote-front).
test(int): extract createShootWithFrame helper + tame fallow on tests
Some checks failed
tests / test (pull_request) Failing after 54s
f82a906a07
Fallow audit on PR #51 flagged two introduced issues:

1. test/int/auth-delete-user.int-spec.ts duplicated the shoot+frame
   creation pattern already present in test/int/shoot.int-spec.ts.
   Extract the pattern into test/int/helpers/fixtures.ts and call it
   from both specs. The helper throws on non-201 so callers do not have
   to repeat the status assertions.

2. The same new spec was flagged as an unused file because Jest
   discovers integration specs via testRegex, not imports, so no other
   module references it. Add .fallowrc.json that disables the
   unused-files rule for everything under test/. The whole test/
   directory is invoked by the Jest runner, never by imports from
   production entry points, so the rule is a structural false positive
   there.

pnpm test:int still passes (101/101).
chore(fallow): also disable unused-exports under test/
Some checks failed
tests / test (pull_request) Has been cancelled
a7376cc0e4
The previous override only silenced unused-files, which left a paired
finding: any export defined in a helper under test/ shows up as
unused-exports because the spec files that import it are themselves
not reachable from production entry points, so Fallow does not count
their imports as "used" references.

unused-exports is the wrong rule to enforce on test helpers; production
code never reaches them by design. Turn it off under test/**/*.ts to
match the unused-files override and unblock the helper extracted in
the previous commit.
chore(fallow): declare test specs as entry points instead of muting rules
All checks were successful
tests / test (pull_request) Successful in 2m32s
91422e9d8d
Replace the rule-suppression overrides with a proper entry-point
declaration. Spec files are roots of the test runner's import graph
(Jest discovers them via testRegex, not imports), so declaring them as
additional Fallow entries makes the dependency graph correct:

- spec files become reachable, so unused-files no longer flags them
- exports they consume (e.g. createShootWithFrame in helpers/fixtures.ts)
  become "used", so unused-exports no longer flags them

This is the structurally honest fix - the helper export is not unused,
Fallow just could not see the importing modules before.
eliott merged commit 0370949a9b into develop 2026-05-16 21:29:46 +02:00
eliott deleted branch audit/legal-compliance 2026-05-16 21:29:46 +02:00
Sign in to join this conversation.
No reviewers
No labels
run-ci
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
RollNote/rollnote-api!51
No description provided.