The default mail driver in config/mail.php is 'log', which had no
matching Vue component, causing the mail configuration step in the
install wizard (and settings page) to render empty.
Cherry-picked from v3.0 branch. Three fixes:
1. Refresh CSRF cookie after logout (auth.js)
2. Clear auth.token and selectedCompany from localStorage on logout (auth.js)
3. Invalidate session and regenerate CSRF token on server-side logout (web.php)
Without these, logging out and back in as a different user would fail
with CSRF token mismatch and 401 Unauthenticated errors because the
browser held stale session cookies and localStorage tokens.
Add scopeWhereCompany() to User model using whereHas through the
user_company pivot table. Apply it in UsersController::index() and
SearchController so users only see members of their current company.
Previously, the users page showed ALL users across all companies.
Ref #574
Verify the source record belongs to the current company before cloning.
Previously, users could clone invoices/estimates from other companies,
leaking sensitive data (amounts, customer details, items, taxes, notes).
The view policy already includes hasCompany() check, so authorizing
view on the source record gates both ability and company ownership.
Ref #574
Bulk delete: filter IDs through whereCompany() before deleting in all
controllers (Invoices, Payments, Items, Expenses, Estimates, Recurring
Invoices). Previously, any user could delete records from other companies
by providing cross-company IDs.
Transfer ownership: fix inverted hasCompany() check that allowed
transferring company ownership to users who do NOT belong to the company,
while blocking users who DO belong.
Ref #567
* Fix CustomerPolicy missing hasCompany() check (cross-company IDOR)
Add $user->hasCompany($customer->company_id) check to view, update,
delete, restore, and forceDelete methods in CustomerPolicy, matching
the pattern used by all other policies (InvoicePolicy, PaymentPolicy,
EstimatePolicy, etc.).
Without this check, a user in Company A with view-customer ability
could access customers belonging to Company B by providing the target
customer's ID.
Add cross-company authorization tests to verify the fix.
Closes#565
* Scope bulk delete to current company to prevent cross-company deletion
Filter customer IDs through whereCompany() before passing to
deleteCustomers(), ensuring users cannot delete customers belonging
to other companies via the bulk delete endpoint.
Update Node.js from 20 to 24 across CI workflows, Dockerfiles,
package.json engines field, and add .node-version file for consistent
local development.