### Environment Setup and Dependency Management Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/contributing/local_testing.md Commands to install necessary system dependencies like Docker and PostgreSQL, and project-specific dependencies using Poetry. ```bash # Install Docker (macOS) brew install --cask docker # Install Poetry curl -sSL https://install.python-poetry.org | python3 - # Install project dependencies poetry install ``` -------------------------------- ### Manage Docusaurus Development and Build Lifecycle Source: https://github.com/kdpisda/django-rls/blob/main/documentation/README.md Commands to install dependencies, start the local development server, build the static site, and serve the production build locally. ```bash # Install dependencies npm install # Start development server npm start # Build the static site npm run build # Test the production build locally npm run serve ``` -------------------------------- ### Implement MDX Tabs Component Source: https://github.com/kdpisda/django-rls/blob/main/documentation/README.md An example of using MDX to create interactive tabs within documentation files for multi-language code support. ```mdx import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ```python # Python code ``` ```bash # Shell commands ``` ``` -------------------------------- ### Install and Configure django-rls Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/tutorials/enterprise-erp/project-setup.md Installs the package via pip and configures the necessary Django settings, including adding the RLS middleware after the authentication middleware. ```bash pip install django-rls ``` ```python INSTALLED_APPS = [ # ... 'django_rls', 'myapp', ] MIDDLEWARE = [ # ... 'django.contrib.auth.middleware.AuthenticationMiddleware', # Add RLS middleware AFTER authentication 'django_rls.middleware.RLSContextMiddleware', ] ``` -------------------------------- ### Run database migrations Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/quick-start.md Executes Django management commands to apply schema changes required by the RLS configuration. ```bash python manage.py makemigrations python manage.py migrate ``` -------------------------------- ### Implement advanced RLS policies Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/quick-start.md Examples of complex RLS implementations including multi-tenant filtering, public/private data visibility, and group-based access control using custom SQL expressions. ```python # Multi-Tenant Application class TenantModel(RLSModel): tenant = models.ForeignKey('Tenant', on_delete=models.CASCADE) class Meta: rls_policies = [ TenantPolicy('tenant_policy', tenant_field='tenant'), ] # Public/Private Data class Document(RLSModel): is_public = models.BooleanField(default=False) owner = models.ForeignKey(User, on_delete=models.CASCADE) class Meta: rls_policies = [ CustomPolicy( 'visibility_policy', expression="is_public = true OR owner_id = current_setting('rls.user_id')::integer" ), ] # Group-Based Access class GroupDocument(RLSModel): group = models.ForeignKey('auth.Group', on_delete=models.CASCADE) class Meta: rls_policies = [ CustomPolicy( 'group_policy', expression=""" group_id IN ( SELECT group_id FROM auth_user_groups WHERE user_id = current_setting('rls.user_id')::integer ) """ ), ] ``` -------------------------------- ### Install Django RLS package Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/quick-start.md Installs the django-rls package via pip to enable row-level security features in your Django project. ```bash pip install django-rls ``` -------------------------------- ### Execute Deployment Script Source: https://github.com/kdpisda/django-rls/blob/main/documentation/README.md A shell command to trigger the manual deployment process for the documentation site. ```bash ./scripts/deploy-docs.sh ``` -------------------------------- ### Verify installation Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/installation.md Use the Django shell to import the package and verify the installed version. This confirms that the library is correctly recognized by the environment. ```bash python manage.py shell ``` ```python import django_rls print(django_rls.__version__) ``` -------------------------------- ### Test Package Installation Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/contributing/releasing.md Verifies the package installation from the TestPyPI repository to ensure the build is functional before production release. ```bash pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple django-rls==VERSION ``` -------------------------------- ### Troubleshooting and Maintenance Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/contributing/local_testing.md Utility commands for debugging database connections, resetting environments, and clearing test caches. ```bash # Check PostgreSQL status pg_isready # Reset test environment pytest --cache-clear rm -rf .coverage htmlcov/ ``` -------------------------------- ### Install Django RLS Package Source: https://github.com/kdpisda/django-rls/blob/main/README.md Commands to install the package via pip from PyPI or directly from the source repository. ```bash pip install django-rls # Or for development version pip install git+https://github.com/kdpisda/django-rls.git ``` -------------------------------- ### Enable RLS and Run Migrations (Bash) Source: https://github.com/kdpisda/django-rls/blob/main/examples/basic_usage/README.md Commands to apply database schema changes for the RLS model and then enable the Row Level Security feature using Django management commands. ```bash python manage.py makemigrations python manage.py migrate python manage.py enable_rls ``` -------------------------------- ### Install django-rls package Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/installation.md Commands to install the django-rls library using standard Python package managers. Ensure you have the required Python and Django versions installed before proceeding. ```bash pip install django-rls ``` ```bash poetry add django-rls ``` -------------------------------- ### Query RLS-protected models Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/quick-start.md Demonstrates that standard Django QuerySet methods automatically respect RLS policies, requiring no manual filtering in views. ```python from django.shortcuts import render from .models import Task def task_list(request): # Users automatically see only their own tasks tasks = Task.objects.all() return render(request, 'tasks/list.html', {'tasks': tasks}) ``` -------------------------------- ### Code Quality and Linting Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/contributing/local_testing.md Commands to ensure code quality using standard Python tools like Black, isort, flake8, and mypy. ```bash # Run quality checks poetry run black --check . poetry run isort --check-only . poetry run flake8 . poetry run mypy django_rls # Auto-fix formatting poetry run black . poetry run isort . ``` -------------------------------- ### Configure Django settings for RLS Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/quick-start.md Registers the django_rls application and adds the required RLSContextMiddleware to the Django settings file. The middleware must be placed after the authentication middleware to function correctly. ```python INSTALLED_APPS = [ # ... your apps 'django_rls', ] MIDDLEWARE = [ # ... other middleware 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django_rls.middleware.RLSContextMiddleware', # Add after auth ] ``` -------------------------------- ### Database Management and Testing Commands Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/contributing/local_testing.md Commands to manage the PostgreSQL database, run test suites, and generate coverage reports. Includes both Make-based workflows and direct CLI commands. ```bash # Start services make docker-up # Run tests pytest --cov=django_rls --cov-report=html # Create test database manually createdb test_django_rls ``` -------------------------------- ### Configure User Profile and Migrations Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/tutorials/enterprise-erp/project-setup.md Defines a UserProfile model to associate users with departments and provides commands to run database migrations. ```python class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True) ``` ```bash python manage.py makemigrations python manage.py migrate ``` -------------------------------- ### Automate RLS Setup in Deployment Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/management-commands.md Shows how to programmatically trigger RLS management commands within a post-deployment script using call_command. ```python from django.core.management import call_command # Enable RLS after migrations call_command('migrate') call_command('enable_rls') ``` -------------------------------- ### Configure Django settings for RLS Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/installation.md Steps to register the application in INSTALLED_APPS and add the RLSContextMiddleware to the middleware stack. The middleware must be placed after the authentication middleware to function correctly. ```python INSTALLED_APPS = [ # ... your other apps 'django_rls', ] ``` ```python MIDDLEWARE = [ # ... other middleware 'django.contrib.auth.middleware.AuthenticationMiddleware', # Add RLS middleware after authentication 'django_rls.middleware.RLSContextMiddleware', ] ``` -------------------------------- ### Handle Anonymous Users and Performance Caching Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/middleware.md Examples for managing anonymous user sessions and optimizing tenant lookups using Django's cache framework. ```python from django.core.cache import cache class CachedTenantMiddleware(RLSContextMiddleware): def _get_tenant_id(self, request): if not request.user.is_authenticated: return None cache_key = f'user_tenant_{request.user.id}' tenant_id = cache.get(cache_key) if tenant_id is None: tenant_id = request.user.profile.tenant_id cache.set(cache_key, tenant_id, 300) return tenant_id ``` -------------------------------- ### Define Markdown Front Matter Source: https://github.com/kdpisda/django-rls/blob/main/documentation/README.md Standard YAML front matter required at the top of documentation files to control sidebar positioning and page titles. ```markdown --- sidebar_position: 1 title: Your Page Title --- ``` -------------------------------- ### Define RLS-enabled models Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/quick-start.md Creates a model inheriting from RLSModel and defines security policies using the Meta class. This ensures that database queries are automatically filtered based on the defined policies. ```python from django.db import models from django.contrib.auth.models import User from django_rls.models import RLSModel from django_rls.policies import UserPolicy class Task(RLSModel): title = models.CharField(max_length=200) description = models.TextField() owner = models.ForeignKey(User, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) class Meta: rls_policies = [ UserPolicy('owner_policy', user_field='owner'), ] ``` -------------------------------- ### Basic Django RLS Testing Setup Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/testing.md Sets up a Django TestCase to test RLS-enabled models. It creates test users, sets the RLS context for a user, creates data owned by that user, and then switches context to another user and creates data for them. This establishes a baseline for testing data isolation. ```python from django.test import TestCase from django.contrib.auth.models import User from django_rls.db.functions import set_rls_context from myapp.models import Document class RLSTestCase(TestCase): def setUp(self): # Create test users self.user1 = User.objects.create_user('user1') self.user2 = User.objects.create_user('user2') # Set RLS context for user1 set_rls_context('user_id', self.user1.id) # Create test data self.doc1 = Document.objects.create( title='User 1 Doc', owner=self.user1 ) # Switch to user2 set_rls_context('user_id', self.user2.id) self.doc2 = Document.objects.create( title='User 2 Doc', owner=self.user2 ) ``` -------------------------------- ### Django RLS User Context Helper Setup Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/examples/user-based.md Configures custom context processors for Django RLS to include user-specific information like staff status. This involves modifying `settings.py` to list RLS context processors and creating a context function in `myapp/context.py`. Requires Django settings and a custom context file. ```python # settings.py RLS_CONTEXT_PROCESSORS = [ 'myapp.context.user_role_context' ] # myapp/context.py def user_role_context(request): if request.user.is_authenticated: return { 'user_is_staff': str(request.user.is_staff).lower() } return {} ``` -------------------------------- ### Access RLS-Secured Data in Views (Python) Source: https://github.com/kdpisda/django-rls/blob/main/examples/basic_usage/README.md Demonstrates how to query `UserDocument` objects in a Django view. Due to the RLS policy, the `UserDocument.objects.all()` queryset will automatically be filtered to only return documents belonging to the currently logged-in user. ```python from django.shortcuts import render from .models import UserDocument def document_list(request): # User will only see their own documents documents = UserDocument.objects.all() return render(request, 'documents/list.html', {'documents': documents}) ``` -------------------------------- ### Define Data Models for RLS Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/tutorials/enterprise-erp/project-setup.md Defines the Department hierarchy, UserPermission ACL table, and the ERPDocument resource which inherits from RLSModel to support row-level security. ```python from django.db import models from django.contrib.auth.models import User from django_rls.models import RLSModel class Department(models.Model): name = models.CharField(max_length=100) parent = models.ForeignKey( 'self', null=True, blank=True, related_name='sub_departments', on_delete=models.CASCADE ) def __str__(self): return self.name class UserPermission(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) document_id = models.IntegerField() can_view = models.BooleanField(default=False) class ERPDocument(RLSModel): title = models.CharField(max_length=100) department = models.ForeignKey(Department, on_delete=models.CASCADE) content = models.TextField() is_confidential = models.BooleanField(default=True) class Meta: rls_policies = [] ``` -------------------------------- ### Pytest Fixtures for RLS Context Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/testing.md Utilizes Pytest fixtures to manage the RLS context during testing. The `user_context` fixture sets the RLS context for a given user before tests run and can include cleanup logic. This promotes cleaner and more reusable test setups. ```python import pytest from django_rls.db.functions import set_rls_context @pytest.fixture def user_context(user): """Set RLS context for a user.""" set_rls_context('user_id', user.id) yield user # Cleanup if needed @pytest.mark.django_db def test_document_list(user_context, client): response = client.get('/documents/') assert response.status_code == 200 ``` -------------------------------- ### RLS Policy and Configuration Errors (Python) Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/api-reference.md Illustrates the exception handling for `PolicyError` and `ConfigurationError` in Django RLS. `PolicyError` is raised for invalid policy configurations, while `ConfigurationError` indicates issues with the RLS setup in Django's settings or model definitions. ```python from django_rls.exceptions import PolicyError, ConfigurationError from django_rls.policies import UserPolicy from django_rls.models import RLSModel try: policy = UserPolicy('invalid_policy', user_field='') except PolicyError as e: print(f"Invalid policy: {e}") try: class BadModel(RLSModel): class Meta: rls_policies = "not_a_list" # Should be a list except ConfigurationError as e: print(f"Configuration error: {e}") ``` -------------------------------- ### Complete Django RLS Example Models Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/examples/basic-usage.md Defines two Django models, Project and Task, both inheriting from RLSModel and featuring different RLS policies. The Project model restricts access to its owner, while the Task model restricts access to the assigned user. ```python from django.db import models from django.contrib.auth.models import User from django.db.models import Q from django_rls.models import RLSModel from django_rls.policies import ModelPolicy, RLS class Project(RLSModel): name = models.CharField(max_length=100) description = models.TextField() owner = models.ForeignKey(User, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) class Meta: rls_policies = [ # Owner access ModelPolicy('owner_access', filters=Q(owner=RLS.user_id())) ] class Task(RLSModel): project = models.ForeignKey(Project, on_delete=models.CASCADE) title = models.CharField(max_length=200) assigned_to = models.ForeignKey(User, on_delete=models.CASCADE) completed = models.BooleanField(default=False) due_date = models.DateField(null=True, blank=True) class Meta: rls_policies = [ # Assigned user access ModelPolicy('assigned_access', filters=Q(assigned_to=RLS.user_id())) ] ``` -------------------------------- ### Manual Release Management with release.sh Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/contributing/releasing.md Commands to perform manual release steps including readiness checks, version bumping, building distributions, and uploading to PyPI repositories. ```bash # Check readiness ./scripts/release.sh check # Bump version ./scripts/release.sh bump patch ./scripts/release.sh bump minor ./scripts/release.sh bump major ./scripts/release.sh bump pre --pre-release alpha # Build and Upload ./scripts/release.sh build ./scripts/release.sh test-pypi ./scripts/release.sh pypi ``` -------------------------------- ### Troubleshooting and Maintenance Commands Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/contributing/releasing.md Utility commands for cleaning build artifacts, verifying the current package version, and resetting the local repository state. ```bash # Clean build artifacts rm -rf dist/ build/ *.egg-info poetry build # Check current version python -c "from django_rls.__version__ import __version__; print(__version__)" # Reset to tag git reset --hard v0.1.0 ``` -------------------------------- ### Get RLS Context Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/api-reference.md Retrieve the value of a PostgreSQL session variable used for Row-Level Security. ```APIDOC ## get_rls_context ### Description Get a PostgreSQL session variable value. ### Method Function call ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```python from django_rls.db.functions import get_rls_context user_id = get_rls_context('user_id') tenant_id = get_rls_context('tenant_id', default=None) ``` ### Response #### Success Response (200) - **value** (Any) - The value of the RLS context variable. #### Response Example ```json { "value": "some_value" } ``` **Parameters:** - `name` (str) - Variable name (without 'rls.' prefix) - `default` (Any) - Default value if variable not set ``` -------------------------------- ### Testing Multi-Tenant Applications with Django RLS Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/testing.md Illustrates testing RLS in a multi-tenant Django application using `RLSTestMixin`. It shows how to set up tenants and users within those tenants, and then use `with_context` to test data isolation between tenants, ensuring users only access data within their assigned tenant. ```python class TenantTestCase(RLSTestMixin, TestCase): def setUp(self): # Create tenants self.tenant1 = Tenant.objects.create(name='Tenant 1') self.tenant2 = Tenant.objects.create(name='Tenant 2') # Create users in different tenants self.user1 = User.objects.create_user('user1') self.user1.profile.tenant = self.tenant1 self.user1.profile.save() def test_tenant_isolation(self): # Set context for tenant1 with self.with_context(user_id=self.user1.id, tenant_id=self.tenant1.id): # Create data in tenant1 TenantModel.objects.create( name='Tenant 1 Data', tenant=self.tenant1 ) # Switch to tenant2 with self.with_context(user_id=self.user2.id, tenant_id=self.tenant2.id): # Should not see tenant1's data data = TenantModel.objects.all() self.assertEqual(data.count(), 0) ``` -------------------------------- ### Customize Docusaurus Theme Colors Source: https://github.com/kdpisda/django-rls/blob/main/documentation/README.md CSS variables used to define the primary color palette for the Docusaurus theme in custom.css. ```css :root { --ifm-color-primary: #2e8555; --ifm-color-primary-dark: #29784c; /* ... other color variables */ } ``` -------------------------------- ### Grant database permissions Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/installation.md SQL command to grant the necessary privileges to the database user. The user requires permissions to manage RLS policies on tables. ```sql -- Connect as superuser GRANT ALL ON DATABASE your_database TO your_user; ``` -------------------------------- ### Configure PostgreSQL database settings Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/installation.md Update the Django DATABASES setting to use the PostgreSQL backend. This is required as django-rls relies on PostgreSQL Row Level Security features. ```python DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'your_database', 'USER': 'your_user', 'PASSWORD': 'your_password', 'HOST': 'localhost', 'PORT': '5432', } } ``` -------------------------------- ### Implement Tenant-Aware Query Filtering Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/examples/tenant-based.md Demonstrates the difference between manual filtering and the recommended RLS approach to prevent data leakage between tenants. ```python def product_list(request): # WRONG: Could expose other tenants' data products = Product.objects.all() # RIGHT: Must always remember tenant filtering products = Product.objects.filter(tenant=request.user.tenant) ``` -------------------------------- ### Subclass RLSContextMiddleware for Custom Logic Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/middleware.md Demonstrates how to extend the middleware to implement custom tenant detection logic or add additional context variables using set_rls_context. ```python from django_rls.middleware import RLSContextMiddleware from django_rls.db.functions import set_rls_context class CustomRLSMiddleware(RLSContextMiddleware): def _get_tenant_id(self, request): host = request.get_host() subdomain = host.split('.')[0] # ... custom lookup logic return tenant_id class ExtendedRLSMiddleware(RLSContextMiddleware): def _set_rls_context(self, request): super()._set_rls_context(request) set_rls_context('department', request.user.profile.department) ``` -------------------------------- ### Recursive Department Sub-department Fetch (Python) Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/tutorials/enterprise-erp/hierarchical-access.md A recursive Python function to find all sub-department IDs starting from a given department. This approach can lead to performance issues due to multiple SQL queries. ```python def get_all_sub_departments(dept): ids = [dept.id] for child in dept.sub_departments.all(): ids.extend(get_all_sub_departments(child)) return ids # views.py def document_list(request): user_dept = request.user.profile.department # PROBLEM: This executes dozens of SQL queries just to build the list! visible_dept_ids = get_all_sub_departments(user_dept) return ERPDocument.objects.filter(department_id__in=visible_dept_ids) ``` -------------------------------- ### Define Tenant and User Models Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/examples/tenant-based.md Defines the core Tenant structure and links users to specific tenants via a foreign key relationship. ```python from django.db import models from django.contrib.auth.models import AbstractUser class Tenant(models.Model): name = models.CharField(max_length=100) subdomain = models.SlugField(unique=True) class User(AbstractUser): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, related_name='users') ``` -------------------------------- ### Complete Django RLS Example Views Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/examples/basic-usage.md Presents a Django view function 'dashboard' that utilizes RLS-protected models. All queries within this view are automatically filtered according to the RLS policies defined in the models. ```python from django.shortcuts import render from django.contrib.auth.decorators import login_required from django.db.models import Count from .models import Project, Task @login_required def dashboard(request): # All queries automatically filtered! context = { 'projects': Project.objects.all(), 'tasks': Task.objects.filter(completed=False), 'completed_tasks': Task.objects.filter(completed=True).count(), 'project_stats': Project.objects.annotate( task_count=Count('task') ), } return render(request, 'dashboard.html', context) ``` -------------------------------- ### Automated RLS Policy Testing with RLSTestMixin Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/tutorials/enterprise-erp/verification.md Automate RLS policy testing using the `RLSTestMixin` provided by django-rls. This mixin simplifies setting up test contexts and asserting expected access control behavior for different user roles and tenant assignments. ```python from django.test import TestCase from django_rls.test import RLSTestMixin class SecurityTest(RLSTestMixin, TestCase): def test_isolation(self): # Create data... # Helper context manager with self.with_context(user_id=self.alice.id, tenant_id=self.sales_dept.id): self.assertFalse(ERPDocument.objects.exists()) def test_auditor_access(self): # Simulate Context Processor injection manually for test # Or rely on integration test if using Client() # Test direct context: with self.with_context(user_email='audit@audit.megacorp.com'): self.assertTrue(ERPDocument.objects.exists()) ``` -------------------------------- ### Define User-Based RLS Model (Python) Source: https://github.com/kdpisda/django-rls/blob/main/examples/basic_usage/README.md Defines a Django model `UserDocument` that inherits from `RLSModel` and enforces user-based Row Level Security using `UserPolicy`. The `user_field` in `Meta.rls_policies` specifies the foreign key to the User model. ```python from django.db import models from django.contrib.auth.models import User from django_rls.models import RLSModel from django_rls.policies import UserPolicy class UserDocument(RLSModel): """Document that belongs to a user.""" title = models.CharField(max_length=200) content = models.TextField() user = models.ForeignKey(User, on_delete=models.CASCADE) created_at = models.DateTimeField(auto_now_add=True) class Meta: rls_policies = [ UserPolicy('user_document_policy', user_field='user'), ] ``` -------------------------------- ### Define Global and Tenant-Specific Products with RLS Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/examples/tenant-based.md Defines Django models for global products and tenant-specific product customizations. TenantProduct uses RLS policies to ensure data isolation based on the tenant. ```python class GlobalProduct(RLSModel): """Products available to all tenants.""" name = models.CharField(max_length=200) base_price = models.DecimalField(max_digits=10, decimal_places=2) # No RLS policies - accessible to all class TenantProduct(RLSModel): """Tenant-specific product customization.""" tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE) global_product = models.ForeignKey(GlobalProduct, on_delete=models.CASCADE) custom_price = models.DecimalField(max_digits=10, decimal_places=2, null=True) is_available = models.BooleanField(default=True) class Meta: rls_policies = [ ModelPolicy( 'tenant_isolation', filters=Q(tenant=RLS.tenant_id()) ), ] unique_together = [['tenant', 'global_product']] ``` -------------------------------- ### Query Data in Tenant-Aware Views Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/examples/tenant-based.md Shows how views become simplified as RLS policies automatically handle tenant isolation for all queries. ```python @login_required def customer_list(request): # Automatically filtered to current tenant! customers = Customer.objects.all() return render(request, 'customers/list.html', {'customers': customers}) ``` -------------------------------- ### Implement Multi-Database RLS Middleware Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/middleware.md Extends the base RLS middleware to apply context settings across multiple database connections. It iterates through specified database aliases and executes raw SQL to set the configuration parameters. ```python class MultiDBRLSMiddleware(RLSContextMiddleware): def _set_rls_context(self, request): # Set context for each database for db_alias in ['default', 'tenant_db']: with connections[db_alias].cursor() as cursor: cursor.execute( "SELECT set_config('rls.user_id', %s, true)", [str(request.user.id)] ) ``` -------------------------------- ### Get RLS Context Value (Python) Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/api-reference.md Retrieves a PostgreSQL session variable value using `get_rls_context`. It takes the variable name and an optional default value if the variable is not set. This is useful for accessing user or tenant IDs within your Django application. ```python from django_rls.db.functions import get_rls_context user_id = get_rls_context('user_id') tenant_id = get_rls_context('tenant_id', default=None) ``` -------------------------------- ### Settings Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/api-reference.md Configuration settings for Django RLS. ```APIDOC ## Settings ### Description Optional settings dict for Django RLS configuration. ### Method Settings Configuration ### Endpoint N/A ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```python # settings.py DJANGO_RLS = { 'DEFAULT_SCHEMA_EDITOR': 'django_rls.backends.postgresql.RLSDatabaseSchemaEditor', 'CONTEXT_PROCESSOR': 'myapp.rls.custom_context_processor', # Deprecated 'AUTO_ENABLE_RLS': True, # Enable RLS automatically in migrations 'POLICY_NAMING_CONVENTION': '{model_name}_{policy_type}_policy', } RLS_CONTEXT_PROCESSORS = [ 'myapp.context.user_email_processor', 'myapp.context.subscription_status_processor', ] ``` ### Response #### Success Response (200) N/A (These are settings) #### Response Example N/A **Settings:** - `DJANGO_RLS` (dict): Optional settings dictionary for Django RLS. - `DEFAULT_SCHEMA_EDITOR` (str): Default schema editor to use. - `CONTEXT_PROCESSOR` (str): Deprecated context processor setting. - `AUTO_ENABLE_RLS` (bool): Automatically enable RLS in migrations. - `POLICY_NAMING_CONVENTION` (str): Convention for naming RLS policies. - `RLS_CONTEXT_PROCESSORS` (list): List of dotted paths to context processor callables. ``` -------------------------------- ### Manual RLS Policy Verification with Django Shell Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/tutorials/enterprise-erp/verification.md Manually verify RLS policies by setting context within the Django shell. This involves importing necessary models and the `set_rls_context` function, creating sample data, and then simulating different user and tenant contexts to observe access control. ```python from myapp.models import ERPDocument, Department from django.contrib.auth.models import User from django_rls.db.functions import set_rls_context # Setup eng = Department.objects.get(name='Engineering') sales = Department.objects.get(name='Sales') alice = User.objects.get(username='alice') # Sales # Create an Engineering Doc doc = ERPDocument.objects.create(title="Top Secret", department=eng) # 1. Simulate Alice (Sales) set_rls_context('user_id', alice.id) set_rls_context('tenant_id', sales.id) print(ERPDocument.objects.count()) # Output: 0 (Correct! Hidden) # 2. Simulate Bob (Engineering VP) bob = User.objects.create(username='bob') set_rls_context('user_id', bob.id) set_rls_context('tenant_id', eng.id) print(ERPDocument.objects.count()) # Output: 1 (Visible!) ``` -------------------------------- ### CI/CD Testing with PostgreSQL and SQLite Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/testing.md Configures CI/CD pipelines to run tests with different database backends. It shows how to execute tests requiring PostgreSQL (e.g., for full RLS functionality) and how to run unit tests that can use SQLite by excluding RLS-specific tests. ```yaml # .github/workflows/test.yml - name: Run tests with PostgreSQL run: | pytest --postgresql env: DATABASE_URL: postgresql://postgres:postgres@localhost/test_db - name: Run unit tests with SQLite run: | pytest -m "not postgresql" ``` -------------------------------- ### Factory Pattern for RLS Test Data Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/testing.md Implements the Factory pattern using `django-factory-boy` to create test data for RLS scenarios. The `RLSFactory` automatically sets the RLS context based on the owner of the created object, simplifying the setup for tests involving user-specific data. ```python import factory from django_rls.test import RLSFactory class DocumentFactory(RLSFactory): class Meta: model = Document title = factory.Faker('sentence') owner = factory.SubFactory(UserFactory) @classmethod def _create(cls, model_class, *args, **kwargs): # Automatically set RLS context from owner owner = kwargs.get('owner') if owner: set_rls_context('user_id', owner.id) return super()._create(model_class, *args, **kwargs) ``` -------------------------------- ### Implement View-Level Logic for Auditor Access Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/tutorials/enterprise-erp/context-processors.md Demonstrates the manual approach of checking user email domains within Django views to determine access levels, which leads to code duplication. ```python def document_list(request): queryset = ERPDocument.objects.all() if request.user.email.endswith('@audit.megacorp.com'): return queryset return queryset.filter(department=request.user.department) ``` -------------------------------- ### Implement Tenant Detection Middleware Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/examples/tenant-based.md Middleware to extract the tenant from the request subdomain and inject it into the request context for RLS processing. ```python class TenantMiddleware: def __call__(self, request): subdomain = request.get_host().split(':')[0].split('.')[0] try: request.tenant = Tenant.objects.get(subdomain=subdomain, is_active=True) except Tenant.DoesNotExist: request.tenant = None return self.get_response(request) ``` -------------------------------- ### Apply Multiple RLS Policies to Model Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/tutorials/enterprise-erp/context-processors.md Demonstrates how to combine multiple RLS policies on a model, where access is granted if any of the policies pass. ```python class ERPDocument(RLSModel): class Meta: rls_policies = [ hierarchy_policy, auditor_policy ] ``` -------------------------------- ### Simplified Document Access with Django-RLS Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/tutorials/enterprise-erp/basic-isolation.md Demonstrates how django-rls simplifies data access in views after setting up the RLS context and policies. The `ERPDocument.objects.all()` call is now automatically filtered by the database according to the defined policy. ```python # views.py (The RLS Way) def document_list(request): # SAFE! automatically filtered by the DB docs = ERPDocument.objects.all() return render(request, 'list.html', {'docs': docs}) ``` -------------------------------- ### Test Custom Policy Logic in Django Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/testing.md Demonstrates how to test custom RLS policies by creating test data, simulating different user access levels (owner, team member, different tenant), and asserting expected access to project objects. This approach verifies that the RLS is correctly enforcing access control rules. ```python def test_complex_policy(self): # Create test data project = Project.objects.create( name='Test Project', owner=self.user1, is_public=False, tenant=self.tenant1 ) project.team.add(self.user2) # Test owner access with self.as_user(self.user1, tenant=self.tenant1): projects = Project.objects.all() self.assertIn(project, projects) # Test team member access with self.as_user(self.user2, tenant=self.tenant1): projects = Project.objects.all() self.assertIn(project, projects) # Test no access from different tenant with self.as_user(self.user3, tenant=self.tenant2): projects = Project.objects.all() self.assertNotIn(project, projects) ``` -------------------------------- ### Trigger Release via Git Tags Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/contributing/releasing.md Initiates the automated release workflow by pushing a versioned git tag to the remote repository. ```bash git tag v1.0.0 git push origin v1.0.0 ``` -------------------------------- ### Optimize Tenant Field Indexing in Django Models Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/examples/tenant-based.md Demonstrates how to add database indexes to Django models that include a tenant field. This improves query performance when filtering or sorting by tenant and other fields. ```python class TenantModel(RLSModel): tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE, db_index=True) # ... other fields class Meta: indexes = [ models.Index(fields=['tenant', '-created_at']), ] ``` -------------------------------- ### SQL Translation of RLS Policies Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/examples/basic-usage.md Illustrates how Django RLS translates Python Q objects into SQL WHERE clauses for PostgreSQL. This shows the underlying mechanism that enforces RLS at the database level. ```sql -- What you write: SELECT * FROM myapp_task; -- What PostgreSQL executes for user_id=1: SELECT * FROM myapp_task WHERE owner_id = current_setting('rls.user_id')::integer; ``` -------------------------------- ### Testing RLS with Django Fixtures Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/testing.md Shows how to test RLS policies when using Django fixtures. It defines a fixture file (`test_rls_data.json`) and a TestCase that loads this fixture. The test then verifies that the RLS context correctly filters the data loaded from the fixture, ensuring policies are respected even with pre-loaded data. ```json # fixtures/test_rls_data.json [ { "model": "myapp.document", "pk": 1, "fields": { "title": "Public Document", "is_public": true, "owner": 1 } } ] ``` ```python class FixtureTestCase(TestCase): fixtures = ['test_rls_data.json'] def test_fixtures_respect_rls(self): # Set context set_rls_context('user_id', '1') # Verify fixture data is filtered docs = Document.objects.all() # Only see documents based on policy ``` -------------------------------- ### Django RLS Test Utilities: Disabling RLS and Impersonating Users Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/testing.md Demonstrates the use of `django_rls.test.RLSTestMixin` to simplify RLS testing. It shows how to temporarily disable RLS for specific models using `disable_rls` and how to test code execution as a specific user using `as_user`. ```python from django_rls.test import RLSTestMixin class DocumentTestCase(RLSTestMixin, TestCase): def test_with_rls_disabled(self): # Temporarily disable RLS with self.disable_rls(Document): # Can see all documents docs = Document.objects.all() self.assertEqual(docs.count(), 2) def test_as_different_user(self): # Test as user1 with self.as_user(self.user1): docs = Document.objects.all() self.assertEqual(docs.count(), 1) # Test as user2 with self.as_user(self.user2): docs = Document.objects.all() self.assertEqual(docs.count(), 1) ``` -------------------------------- ### Create Custom RLS Management Commands Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/guides/management-commands.md Demonstrates how to extend Django management commands to perform custom logic, such as auditing models for RLS coverage. ```python from django.core.management.base import BaseCommand from django.apps import apps from django_rls.models import RLSModel class Command(BaseCommand): help = 'Check which models should have RLS but don\'t' def handle(self, *args, **options): for model in apps.get_models(): if issubclass(model, RLSModel): continue fields = model._meta.get_fields() has_user = any(f.name in ['user', 'owner', 'created_by'] for f in fields) has_tenant = any(f.name in ['tenant', 'organization'] for f in fields) if has_user or has_tenant: self.stdout.write( self.style.WARNING( f'{model._meta.label} could benefit from RLS' ) ) ``` -------------------------------- ### Complex Q Objects for Document Access (Django) Source: https://github.com/kdpisda/django-rls/blob/main/documentation/docs/tutorials/enterprise-erp/granular-acls.md Demonstrates a traditional Django approach to granting document access by filtering documents belonging to the user's department or those explicitly shared via a UserPermission table. This method can lead to complex OR logic and performance issues due to the use of .distinct(). ```python from django.db.models import Q # views.py def document_list(request): user = request.user # "Get my dept docs OR docs I have permission for" return ERPDocument.objects.filter( Q(department=user.department) | Q(userpermission__user=user, userpermission__can_view=True) ).distinct() # Distinct is needed because joins can duplicate rows! ``` -------------------------------- ### Configure Django Settings for RLS Source: https://github.com/kdpisda/django-rls/blob/main/README.md Required configuration to enable the RLS middleware and register the application in the Django settings file. ```python INSTALLED_APPS = [ # ... your apps 'django_rls', ] MIDDLEWARE = [ # ... your middleware 'django_rls.middleware.RLSContextMiddleware', ] ```