Skip to content

fix: make clients shared across all users while keeping work entries per-user#462

Open
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1776164712-fix-shared-clients
Open

fix: make clients shared across all users while keeping work entries per-user#462
devin-ai-integration[bot] wants to merge 1 commit intomainfrom
devin/1776164712-fix-shared-clients

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot commented Apr 14, 2026

Summary

Clients were scoped per-user via a user_email foreign key and WHERE user_email = ? filtering on every query. This meant clients created by one user were invisible to others after login.

This PR makes clients a shared resource:

  • Schema: Renamed clients.user_email (NOT NULL) → clients.created_by (nullable). Changed FK from ON DELETE CASCADE to ON DELETE SET NULL so deleting a user doesn't destroy shared clients.
  • Client routes: Removed user_email filtering from all CRUD operations in clients.js.
  • Work entry routes: Removed user_email from client-existence checks when creating/updating work entries. Work entries themselves remain per-user (work_entries.user_email is unchanged).
  • Report routes: Removed user_email from client lookups. Work entry queries in reports still filter by user_email.
  • Tests: Updated all assertions to match new shared-client behavior (8 files changed).

Review & Testing Checklist for Human

  • DELETE /api/clients (bulk delete) now deletes ALL clients globally — previously scoped to the authenticated user's clients only. Verify this is acceptable; if not, this endpoint may need to be removed or restricted.
  • Any authenticated user can now update or delete any client — ownership checks were fully removed. Confirm there's no need for role-based or creator-only edit/delete restrictions.
  • Verify end-to-end: Log in as User A, create a client. Log out, log in as User B — confirm the client is visible. Create a work entry under User B against that client. Log back in as User A — confirm User A sees the client but does NOT see User B's work entries.

Notes

  • The database is in-memory SQLite, so no migration is needed — schema changes apply on next restart.
  • Error messages simplified from "Client not found or does not belong to user" to "Client not found".

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/ddcca4ce78c24cda8e35c5d79521c2d0


Open with Devin

…per-user

Root cause: clients table and all client queries were scoped by user_email,
making clients invisible to other users.

Changes:
- Rename clients.user_email to created_by (nullable, for auditing only)
- Remove user_email filtering from all client CRUD queries
- Remove user_email filtering from client lookups in reports and work entries
- Keep work_entries.user_email filtering intact (entries remain per-user)
- Update tests to match new shared-client behavior
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown
Contributor Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines 193 to +195
db.run(
'DELETE FROM clients WHERE user_email = ?',
[req.userEmail],
'DELETE FROM clients',
[],
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🔴 DELETE / endpoint wipes ALL clients and cascades to ALL users' work entries

The DELETE / endpoint changed from DELETE FROM clients WHERE user_email = ? (scoped to the authenticated user) to DELETE FROM clients with no WHERE clause. Any single authenticated user calling this endpoint will delete every client in the entire system. Because work_entries has FOREIGN KEY (client_id) REFERENCES clients (id) ON DELETE CASCADE (backend/src/database/init.js:64), this also cascade-deletes ALL work entries for ALL users — a complete system-wide data wipe triggered by any authenticated user.

Prompt for agents
The DELETE / endpoint on the clients router now runs an unconditional DELETE FROM clients, wiping all clients and cascade-deleting all work entries system-wide. This was previously scoped to the authenticated user's clients (WHERE user_email = ?). In a shared-clients model, this endpoint should either be removed entirely, restricted to an admin role, or at minimum require some additional authorization check. Consider removing this mass-delete endpoint or adding role-based access control. The relevant code is in backend/src/routes/clients.js lines 189-208, and the CASCADE FK is defined in backend/src/database/init.js line 64.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +222 to +223
'SELECT id FROM clients WHERE id = ?',
[clientId],
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🔴 Any user can delete a shared client, cascade-deleting other users' work entries

The DELETE /:id endpoint now allows any authenticated user to delete any client (no ownership check). Because clients are now shared and multiple users can have work entries referencing the same client, the ON DELETE CASCADE on work_entries.client_id (backend/src/database/init.js:64) means deleting a shared client destroys other users' work entries — unauthorized cross-user data deletion. Previously, only the client owner could delete their own client, and CASCADE only affected their own work entries.

Prompt for agents
In the shared-clients model, deleting a client cascades to delete work_entries for ALL users who logged time against that client, not just the deleting user. The fix should either: (1) restrict client deletion to the creator (check created_by = req.userEmail), (2) add an admin-only gate, or (3) change the ON DELETE behavior from CASCADE to something safer (e.g., SET NULL or RESTRICT) on work_entries.client_id, so that clients with work entries from other users cannot be deleted. The relevant code is in backend/src/routes/clients.js lines 220-248, the cascade FK in backend/src/database/init.js line 64, and the schema definition in backend/src/database/init.js lines 53-67.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

0 participants