fix: make clients shared across all users while keeping work entries per-user#462
fix: make clients shared across all users while keeping work entries per-user#462devin-ai-integration[bot] wants to merge 1 commit intomainfrom
Conversation
…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 EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| db.run( | ||
| 'DELETE FROM clients WHERE user_email = ?', | ||
| [req.userEmail], | ||
| 'DELETE FROM clients', | ||
| [], |
There was a problem hiding this comment.
🔴 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.
Was this helpful? React with 👍 or 👎 to provide feedback.
| 'SELECT id FROM clients WHERE id = ?', | ||
| [clientId], |
There was a problem hiding this comment.
🔴 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.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Clients were scoped per-user via a
user_emailforeign key andWHERE 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:
clients.user_email(NOT NULL) →clients.created_by(nullable). Changed FK fromON DELETE CASCADEtoON DELETE SET NULLso deleting a user doesn't destroy shared clients.user_emailfiltering from all CRUD operations inclients.js.user_emailfrom client-existence checks when creating/updating work entries. Work entries themselves remain per-user (work_entries.user_emailis unchanged).user_emailfrom client lookups. Work entry queries in reports still filter byuser_email.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.Notes
"Client not found or does not belong to user"to"Client not found".Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/ddcca4ce78c24cda8e35c5d79521c2d0