Skip to content

Follow up on data deletions in official clients - #1012

Merged
Portals merged 13 commits into
mainfrom
t3/grill-issue-884
Nov 14, 2026
Merged

Portals merged 13 commits into
mainfrom
t3/grill-issue-884

Conversation

@Portals

@Portals Portals commented Nov 8, 2026 •

Copy link
Copy Markdown
Member

Closes #884

What this does

When a user deletes their account, an admin deletes a user, or a user retracts their approval of an official client, Gamma now records one data deletion request per (user, official client). Admins use the requests to make sure each official client deletes the data it has about the user.

Recording

  • Requests are created in the same transaction as the deletion and before it, since g_user_approval cascades when the user is deleted. The user row is locked with SELECT ... FOR UPDATE first, so an approval saved at the same moment is either recorded or refused.
  • Each request stores the user id, cid, client uid, a snapshot of the client name, the reason (ACCOUNT_DELETED / APPROVAL_REVOKED) and when it was created.
  • If the user approves the client again later, the request is marked "Approved the client again" but stays open.

Admin page /data-deletion (new "Data deletion" in the admin nav)

  • Open requests show the cid and user id, the client, the reason and the deadline: one calendar month after creation, Europe/Stockholm, as required by the GDPR. Overdue requests are flagged.
  • Any admin can clear a request, after a confirmation. Clearing removes the cid and client name and records who cleared it and when. Cleared requests are deleted after one year. Open requests are never deleted automatically.
  • Admins manage the recipients of the daily email on the same page.

Daily email and banner

  • While requests are open, a daily email is sent through Gotify to the recipients. It contains only counts, deadlines with overdue ones flagged, and a link, with no personal data. Each send is logged. The schedule is set with DATA_DELETION_DIGEST_CRON (default 0 0 8 * * *, Europe/Stockholm).
  • Scheduled mail has no request IP to throttle by, so it goes through the new MailService.sendSystemMail.
  • Admins see an Action required banner on every page when no recipients are configured, or when any request is overdue.

Users and client owners

  • /delete-your-account lists the official clients the user approved and explains what is kept and for how long. Users whose earlier requests have already been cleared are told that a record of the deletion is kept for one year. For clients made by other users, it shows a prefilled mailto: link per owner, grouped by owner.
  • The consent page for a user-made client now shows the owner's email, so the user can ask questions. Only users signed in to Gamma can reach this page.
  • The create client page and "Your clients" tell owners where their email is shown, and that it is never shown to anyone who isn't signed in.
  • New approvals of a user-made client whose owner is locked are refused. The consent page shows "Client can't be approved" instead of the 500 it showed before, and a posted consent gets access_denied (400). Users who already approved keep working.
  • Other users' emails are read through narrow queries (ClientRepository.getUserOwnerEmail / isUserOwnerLocked), since UserRepository hides other users' extended data.

Testing

  • New e2e tests:
    • data-deletion-account-delete-creates-request: delete page, owner mailto, consent page email, banner, adding a recipient, clearing.
    • data-deletion-retract-approval-creates-request
    • data-deletion-daily-email: the email arrives and has no personal data.
    • data-deletion-cleared-request-retention-notice: a user with only cleared requests is still told what is kept.
    • oauth-locked-owner-blocks-new-approvals
  • /data-deletion and /delete-your-account were added to the accessibility scan.

Demos

Recorded from commit 85396a6 with fictional test users and clients.

1. Account deletion and admin follow-up (36 seconds)

Retention information and client-owner contact, account deletion, the admin warning banner, daily-email recipient setup, and clearing a request after confirming downstream erasure.

01-account-and-admin.mp4

2. Withdrawal, retained contacts and reapproval (39 seconds)

The unofficial-client owner's email, contact reminders surviving withdrawal and reload, official-client erasure follow-up requests, and reapproval leaving an existing request open.

02-withdrawal-and-reapproval.mp4

3. Client-owner deletion acknowledgement (25 seconds)

Owned clients and the requirement to erase collected personal information, deletion blocked without acknowledgement, and successful deletion after checking the acknowledgement.

03-client-owner-acknowledgement.mp4

When a user deletes their account, an admin deletes a user, or a user
retracts their approval of an official client, Gamma now records a data
deletion request for each official client. Admins clear them on the new
data deletion page, within the GDPR deadline of one month. Clearing keeps
only the ids, which are removed after a year.

While requests are open, a daily email with the deadlines and a link is
sent to the configured recipients, and admins see an "Action required"
banner when a request is overdue or no one gets the email.

The delete account page lists the official clients and links to the
owners of the other approved clients. The consent page shows the owner's
email to signed in users, and new approvals of clients whose owner is
locked are refused.

Closes #884

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Portals
Portals requested a review from Goosetaf as a code owner November 8, 2026 00:57
Portals and others added 4 commits November 8, 2026 12:20
# Conflicts:
#	app/src/main/java/it/chalmers/gamma/ScheduledTasks.java
#	app/src/main/java/it/chalmers/gamma/adapter/secondary/jpa/user/UserJpaRepository.java
#	app/src/main/java/it/chalmers/gamma/adapter/secondary/mail/GotifyMailService.java
#	app/src/main/java/it/chalmers/gamma/app/mail/domain/MailService.java
#	app/src/main/java/it/chalmers/gamma/app/user/MeFacade.java
#	e2e/helpers/gamma.ts
#	e2e/helpers/mail.ts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tent

The banner now takes the card width on pages made of cards, and the full
width on pages with tables. The daily email section on the data deletion
page is laid out like the rest of the page, and empty tables are hidden.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Consent saves do not share the revocation user lock, allowing concurrent re-approval to leave an open deletion request unmarked despite renewed consent.

4 open issues
What changed in this PR

Adds follow-up tracking for data deletion in Gamma’s official clients, connecting account deletion and approval retraction to an admin-managed workflow.

Changes:

  • Records deletion requests, tracks re-approvals and clearance, and schedules reminders and retention cleanup.
  • Adds an admin dashboard and warning banner, plus user-facing retention information and client-owner contact links.
  • Blocks new approvals for locked client owners and adds end-to-end and accessibility coverage.
File Description
e2e/​tests/​ui/​oauth-locked-owner-blocks-new-approvals.it.ts Tests locked-owner consent restrictions.
e2e/​tests/​ui/​data-deletion-retract-approval-creates-request.it.ts Tests retraction and re-approval tracking.
e2e/​tests/​ui/​data-deletion-daily-email.it.ts Tests reminder delivery and privacy.
e2e/​tests/​ui/​data-deletion-account-delete-creates-request.it.ts Tests deletion, owner contacts, and admin clearance.
e2e/​tests/​ui/​accessibility-wcag-scan.it.ts Adds deletion pages to accessibility scans.
e2e/​helpers/​test-fixtures.ts Supports custom test environment variables.
e2e/​helpers/​oauth.ts Adds pre-consent assertions.
app/​src/​main/​resources/​templates/​pages/​my-clients.html Explains owner-email visibility.
app/​src/​main/​resources/​templates/​pages/​delete-your-account.html Shows approved clients and retention information.
app/​src/​main/​resources/​templates/​pages/​data-deletion.html Adds request and recipient management UI.
app/​src/​main/​resources/​templates/​pages/​create-user-client.html Discloses owner-email sharing.
app/​src/​main/​resources/​templates/​pages/​authorize.html Displays client-owner contact details.
app/​src/​main/​resources/​templates/​common/​header.html Adds admin navigation and warning banner.
app/​src/​main/​resources/​static/​css/​main.css Styles the banner and deletion dashboard.
app/​src/​main/​resources/​db/​migration/​V9__DATA_DELETION_REQUESTS.sql Creates request and recipient tables.
app/​src/​main/​resources/​application.yml Configures reminder scheduling.
app/​src/​main/​java/​it/​chalmers/​gamma/​ScheduledTasks.java Schedules reminders and cleared-request cleanup.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​user/​UserFacade.java Records requests during admin user deletion.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​user/​MeFacade.java Integrates deletion tracking and client-contact summaries.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​user/​domain/​UserRepository.java Defines the user-lock operation.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​oauth2/​GammaAuthorizationConsentService.java Checks owner locks and records re-approvals.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​mail/​domain/​MailService.java Defines system-mail delivery.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​datadeletion/​domain/​DataDeletionRequestRepository.java Defines request persistence operations.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​datadeletion/​domain/​DataDeletionRequestId.java Adds typed request identifiers.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​datadeletion/​domain/​DataDeletionRequest.java Models request state and deadlines.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​datadeletion/​domain/​DataDeletionRecipientRepository.java Defines recipient management operations.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​datadeletion/​domain/​DataDeletionReason.java Defines deletion reasons.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​datadeletion/​DataDeletionRecorder.java Records deletion and re-approval events.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​datadeletion/​DataDeletionFacade.java Provides admin operations and dashboard data.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​datadeletion/​DataDeletionDigestService.java Builds reminders and removes expired cleared records.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​client/​domain/​ClientRepository.java Defines owner-email and lock queries.
app/​src/​main/​java/​it/​chalmers/​gamma/​app/​client/​ClientFacade.java Exposes guarded owner-contact and lock lookups.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​secondary/​mail/​GotifyMailService.java Supports system mail with delivery results.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​secondary/​jpa/​user/​UserRepositoryAdapter.java Implements user locking.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​secondary/​jpa/​user/​UserJpaRepository.java Adds locking and narrow owner-data queries.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​secondary/​jpa/​datadeletion/​DataDeletionRequestRepositoryAdapter.java Implements request persistence and clearance.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​secondary/​jpa/​datadeletion/​DataDeletionRequestJpaRepository.java Adds request queries and state updates.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​secondary/​jpa/​datadeletion/​DataDeletionRequestEntity.java Maps stored deletion requests.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​secondary/​jpa/​datadeletion/​DataDeletionRecipientRepositoryAdapter.java Implements recipient storage and normalization.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​secondary/​jpa/​datadeletion/​DataDeletionRecipientJpaRepository.java Adds recipient persistence access.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​secondary/​jpa/​datadeletion/​DataDeletionRecipientEntity.java Maps reminder recipients.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​secondary/​jpa/​client/​ClientRepositoryAdapter.java Implements owner-email and lock lookups.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​primary/​web/​ThymeleafAdvice.java Supplies admin banner data.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​primary/​web/​MailtoLink.java Encodes prefilled email subjects and bodies.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​primary/​web/​DeleteYourAccountController.java Builds client lists and owner-contact links.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​primary/​web/​DataDeletionController.java Handles dashboard and management requests.
app/​src/​main/​java/​it/​chalmers/​gamma/​adapter/​primary/​web/​ConsentController.java Shows owner contacts and locked-client messaging.

Review effort: Balanced

Portals and others added 5 commits November 8, 2026 13:54
Consent saves take the same user lock as retractions and account
deletions, so a re-approval is always marked on the request it follows.
Gotify requests time out instead of blocking the daily email, too long
recipient emails are refused, and the delete account page also lists
official clients with open requests from earlier retractions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	app/src/main/java/it/chalmers/gamma/app/user/UserFacade.java
The retention notice was only shown when the user had approved official
clients or had open requests. A user whose requests had all been cleared
saw no notice, even though their user id and client ids are kept for a
year after clearing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Portals and others added 3 commits November 13, 2026 20:53
Put what Gamma keeps after the account is deleted under one heading, and
say that the records for cleared requests come from approvals the user
retracted earlier.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Revoke stored grants and sessions, serialize issuance with withdrawal and deletion, and cascade deletion through owned clients. Queue avatar cleanup with retries, retain dismissible owner contacts after withdrawal, and require client owners to acknowledge erasure before deleting their account.

Clarify retention and erasure notices, document operational duties, and cover migration, access, revocation races and storage cleanup with integration tests.
@Portals
Portals merged commit 5902794 into main Nov 14, 2026
2 checks passed
@Portals
Portals deleted the t3/grill-issue-884 branch November 14, 2026 13:10
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.

Show last deleted accounts along with what official clients they approve

2 participants