feat: implement tenant isolation with PostgreSQL RLS
- users + tenant_memberships tables (tenant-scoped)
- RLS policy on tenant_memberships using set_config('app.current_tenant_id', ...)
- withTenantContext() helper for transaction-scoped tenant context
- separate non-superuser app role (b2bcall_app): the default Docker postgres
user is SUPERUSER and always bypasses RLS even with FORCE, so the app must
never connect through the migration/owner role. Documented in
docs/TENANT_ISOLATION.md.
- automated isolation test proving tenant A never sees tenant B's data
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
-- The Docker Postgres image always makes the initial user (POSTGRES_USER,
|
||||
-- e.g. "b2bcall") a SUPERUSER. Superusers (and BYPASSRLS roles) always
|
||||
-- bypass Row Level Security, no matter FORCE ROW LEVEL SECURITY — so the
|
||||
-- application must NEVER connect as that role for normal queries.
|
||||
--
|
||||
-- This migration creates a separate, unprivileged role for the running
|
||||
-- application. Only this role should be used for APP_DATABASE_URL. The
|
||||
-- superuser role stays reserved for migrations/schema changes (DATABASE_URL
|
||||
-- used by `prisma migrate`).
|
||||
--
|
||||
-- The role's password is set out-of-band (scripts/db-setup-app-role.sh),
|
||||
-- never embedded in a migration file that gets committed to Git.
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'b2bcall_app') THEN
|
||||
CREATE ROLE b2bcall_app LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOBYPASSRLS;
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
GRANT USAGE ON SCHEMA public TO b2bcall_app;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO b2bcall_app;
|
||||
|
||||
-- Applies automatically to tables created by future migrations (run as the
|
||||
-- same owner role), so we don't need to repeat these grants every time.
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO b2bcall_app;
|
||||
Reference in New Issue
Block a user