Superadmin impersonation, hardened auth/upload paths, dependency updates
- Add superadmin impersonation: sessions.impersonated_by (migration 0003) is stamped onto audit rows so impersonated actions are attributable, with a persistent ImpersonationBanner in the app shell. - Harden auth and upload handling across routes (safe redirect targets, filename sanitization, checkout grant handling). - Update dependencies: Sentry 8 -> 10, @fastify/static 8 -> 10, react-router-dom 6.30.6; add find-my-way / fast-uri overrides. - Add tests for safe-next, checkout-grant, and upload-filename. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4a1122a7c9
commit
eb36b81dc9
@@ -8,6 +8,10 @@ import { getStripe, getPlanConfig, stripeIsConfigured } from '../lib/stripe';
|
||||
export async function billingRoutes(app: FastifyInstance) {
|
||||
app.addHook('preHandler', app.requireFirm);
|
||||
|
||||
// Reading plan state is fine for anyone in the firm; spending money or opening the Stripe
|
||||
// portal (which exposes payment methods and invoice history) is the owner's call alone.
|
||||
const ownerOnly = { preHandler: app.requireRole('owner') };
|
||||
|
||||
// Status — what does the UI need to show? Configured at all? Current plan? Has subscription?
|
||||
app.get('/api/billing/status', async (req) => {
|
||||
const firmId = req.user!.firmId!;
|
||||
@@ -21,7 +25,7 @@ export async function billingRoutes(app: FastifyInstance) {
|
||||
});
|
||||
|
||||
// Create a Checkout Session — returns the URL to redirect the user to.
|
||||
app.post('/api/billing/checkout', async (req, reply) => {
|
||||
app.post('/api/billing/checkout', ownerOnly, async (req, reply) => {
|
||||
const parsed = z
|
||||
.object({ plan: z.enum(['pro', 'lifetime']) })
|
||||
.safeParse(req.body);
|
||||
@@ -69,7 +73,7 @@ export async function billingRoutes(app: FastifyInstance) {
|
||||
});
|
||||
|
||||
// Customer Portal — for managing the subscription, updating payment method, viewing invoices.
|
||||
app.post('/api/billing/portal', async (req, reply) => {
|
||||
app.post('/api/billing/portal', ownerOnly, async (req, reply) => {
|
||||
if (!stripeIsConfigured()) return reply.code(503).send({ error: 'stripe_not_configured' });
|
||||
|
||||
const firmId = req.user!.firmId!;
|
||||
|
||||
Reference in New Issue
Block a user