Django-rls 2.0.0 — Major security release + updated documentation

Hi everyone,

We’re excited to announce django-rls 1.0.0, a major security-focused release for PostgreSQL Row Level Security in Django. This is the release we’ve been building toward since 0.4.x: stronger defaults, clearer trust boundaries, and a full security regression suite.

If you’re on 0.4.x, please read the migration section below — 1.0.0 is not drop-in compatible for every app.

What’s new in 1.0.0

Security hardening

  • Identity immutability — user_id and tenant_id can’t be silently overridden after middleware sets them. Use system_rls_context() for admin jobs, tests, and workers that need to act as another user.
  • Session tenant disabled by default — middleware no longer reads request.session["tenant_id"] unless you explicitly opt in with ALLOW_SESSION_TENANT.
  • Connection hygiene — stale RLS context is cleared on pooled connections at the start of each request.
  • Stricter CustomPolicy validation — blocks whitespace-only expressions and DML keywords in addition to the existing DDL blocklist.
  • FORCE ROW LEVEL SECURITY — always applied when RLS is enabled.

New tooling & configuration

  • DJANGO_RLS settings — REQUIRE_CONTEXT, AUDIT_LOG, STRICT_MIGRATE_RLS, ALLOW_SESSION_TENANT, TENANT_MEMBERSHIP_VALIDATOR, REGISTERED_CONTEXT_KEYS, and more.
  • audit_rls management command — run before deploy to verify RLS/FORCE RLS and flag risky CustomPolicy definitions.
  • django_rls.context module — centralized, auditable context management (imports from django_rls.db.functions still work).
  • 109 security regression tests — run against real PostgreSQL, not mocks.

Other improvements

  • Policy context reads use the InitPlan pattern for better performance.
  • AUTO_ENABLE_RLS=False is now correctly respected.
  • Updated threat model and production checklist in .github/SECURITY.md.

Breaking changes (0.4.x → 1.0.0)

Area 0.4.x 1.0.0
Session tenant_id Used by default Off unless ALLOW_SESSION_TENANT=True
Override user_id / tenant_id Allowed Immutable unless system_rls_context()
Connection pooling Context could persist Reset on connect + each request
CustomPolicy validation DDL blocklist Also rejects whitespace-only + DML keywords

Quick migration checklist

  1. Replace ad-hoc set_rls_context("user_id", …) with system_rls_context().
  2. If you used session-based tenants, enable ALLOW_SESSION_TENANT and add TENANT_MEMBERSHIP_VALIDATOR.
  3. Register custom context keys with REGISTERED_CONTEXT_KEYS if you use connection pooling.
  4. Turn on production hardening: REQUIRE_CONTEXT, AUDIT_LOG, STRICT_MIGRATE_RLS.
  5. Add python manage.py audit_rls to your CI/deploy pipeline.

Full step-by-step guide: Migrating from 0.4.x

Updated documentation

The docs site at django-rls.com now has versioned documentation:

Local testing docs now reflect the real PostgreSQL workflow (docker compose + make test).

Install / upgrade

pip install django-rls==1.0.0

Or in requirements.txt / pyproject.toml:

django-rls>=1.0.0,<2.0.0

Staying on 0.4.x? Pin explicitly:

pip install "django-rls>=0.4.1,<1.0.0"

Docs for 0.4 remain at Introduction | Django RLS ( Introduction | Django RLS ).

Links

• Documentation: https://django-rls.com
• PyPI: Client Challenge
• GitHub: GitHub - kdpisda/django-rls: Row Level Security for Django · GitHub
• Changelog: django-rls/CHANGELOG.md at main · kdpisda/django-rls · GitHub
• Security policy: django-rls/.github/SECURITY.md at main · kdpisda/django-rls · GitHub

We want your feedback

If you hit migration issues, have questions about DJANGO_RLS settings, or want to share how you’re using RLS in production — reply here or open an issue on GitHub. Reports of edge cases around connection pooling, Celery workers, or multi-tenant session handling are especially welcome.

Thanks for using django-rls.

— Kuldeep