django-compliance-shield
Drop-in Django library for global privacy compliance. One decorator. Full DPDP, GDPR, CCPA, FCRA compliance.
What it does
Add one decorator to your model. The library handles everything else.
from compliance_shield.decorators import sensitive_field
class UserProfile(models.Model):
data_region = models.CharField(max_length=10, default='IN')
@sensitive_field(field_type='pan', jurisdiction_field='data_region')
class pan_number:
pass
@sensitive_field(field_type='ssn', jurisdiction_field='data_region')
class ssn_number:
pass
That is all you write. The library automatically creates:
| Created | What it does |
|---|---|
_pan_number |
Encrypted storage field (Fernet, regional key) |
pan_number_index |
Blind index for search without decryption |
pan_number |
Property — decrypts on get, encrypts on set |
pan_number_masked |
Property — e.g. ******1234F |
| Access log entry | Every read logged to SensitiveDataAccessLog |
Features
Encryption
- Field-level Fernet encryption with separate keys per jurisdiction
- Blind index for searching encrypted fields
- Auto-masked property (
field_masked) - Key rotation via
python manage.py rotate_keys - MultiFernet supports zero-downtime key rotation
Consent Management
ConsentRecordstores exact text shown, version, IP, timestamp- Consent gate middleware — redirects to consent page if required consents missing
- Withdraw consent — triggers
DataDeletionRequestautomatically - Privacy settings page — users manage all consents in one place
Data Subject Rights
DataSubjectRequesthandles all rights across all jurisdictions- Auto-calculated deadlines: IN=30d, US access=45d, FCRA=30d, EU=30d
- High-sensitivity requests require identity verification
- DSR confirmation email sent automatically
Data Retention
- 29 retention policies seeded out of the box
- Daily enforcement:
python manage.py enforce_retention DataRetentionLog— immutable audit trail of every enforcement action- Legal hold flag prevents deletion of records under investigation
Breach Notification
DataBreachRecordtracks every breach- Authority notification deadlines per jurisdiction (72h IN/EU/UK/US, 30d AU)
- Admin dashboard shows overdue notifications in red
Compliance Middleware
- Jurisdiction detection on every request
- Security headers:
X-Data-Region,X-Frame-Options,X-Content-Type-Options,Referrer-Policy,Permissions-Policy,Strict-Transport-Security
Django Admin
- Admin registration for all 7 compliance models
- Bulk actions: mark DSR in progress / completed, mark breach contained / resolved
- Consent records and access logs are permanently read-only
Jurisdiction coverage
| Code | Law | DSR Deadline | Breach Deadline |
|---|---|---|---|
| IN | DPDP Act 2025 | 30 days | 72 hours |
| US | CCPA / FCRA | 45 days | 72 hours |
| EU | GDPR | 30 days | 72 hours |
| UK | UK GDPR | 30 days | 72 hours |
| CA | PIPEDA | 30 days | ASAP |
| AU | Privacy Act 1988 | 30 days | 30 days |
| AE | UAE PDPL | 30 days | 72 hours |
| SA | Saudi Arabia PDPL | 30 days | 72 hours |
What's new in v1.2.0
Security fixes
Two security bugs fixed — upgrade immediately if using v1.1.0:
- Access log data leak —
GET /api/compliance/access-log/previously returned logs matching any model whose name contained the user class name (e.g."User"), allowing a user to see other users' field-access history. Now filtered strictly by the requesting user's PK. - Open redirect — the
?next=parameter in the consent flow was not validated against the current host. Fixed withurl_has_allowed_host_and_scheme.
Audit trail: who accessed what (GDPR Art.30)
Every SensitiveDataAccessLog entry now records which authenticated user triggered the
field decryption. The middleware sets a thread-local on every request; the descriptor
reads it automatically. Requires migration 0002_audit_accessed_by.
python manage.py migrate
Retention handlers — enforce PII deletion/anonymisation
enforce_retention can now act on your app's own data categories.
Subclass BaseRetentionHandler and register it:
from compliance_shield import BaseRetentionHandler
class PanAadhaarHandler(BaseRetentionHandler):
def get_queryset(self, cutoff):
return UserProfile.objects.filter(created_at__lt=cutoff)
def anonymise(self, queryset):
queryset.update(_pan_number=None, pan_number_index=None)
COMPLIANCE_SHIELD = {
'RETENTION_HANDLERS': {
'pan_aadhaar': 'myapp.handlers.PanAadhaarHandler',
},
}
Privacy policy version re-consent enforcement
Bumping PRIVACY_POLICY_VERSION now actually prompts users to re-consent.
Previously the middleware accepted any old-version consent regardless of the configured version.
Custom profile relation names
COMPLIANCE_SHIELD = {
# Override if your app uses a different profile accessor name
'PROFILE_RELATIONS': ['account', 'worker'],
}
What's new in v1.1.0
1. Email notifications, DRF API, jurisdiction control
COMPLIANCE_SHIELD = {
# Only activate India and USA compliance.
# Consent gate, retention policies, DSR types, and breach deadlines
# will only apply to these two jurisdictions.
'ENABLED_JURISDICTIONS': ['IN', 'US'], # None = all (default)
}
python manage.py compliance_setup will only seed policies for enabled jurisdictions.
The middleware consent gate will only fire for users in enabled jurisdictions.
The privacy settings page will only show DSR types relevant to enabled jurisdictions.
2. Email notifications — fully configurable
COMPLIANCE_SHIELD = {
'EMAIL_NOTIFICATIONS': True, # master switch (default: False)
'EMAIL_FROM': 'compliance@co.com', # defaults to DEFAULT_FROM_EMAIL
# Who gets notified when a user submits a DSR
'DSR_ALERT_RECIPIENTS': ['privacy@co.com'],
# Who gets notified immediately when a breach is recorded
'BREACH_ALERT_RECIPIENTS': ['dpo@co.com', 'legal@co.com'],
# Who gets the daily overdue DSR digest (from enforce_retention cron)
'OVERDUE_DSR_RECIPIENTS': ['privacy@co.com'],
# Whether to email the user on DSR submit / complete / reject
'DSR_USER_CONFIRMATION_EMAIL': True,
}
All notifications use Django's standard email backend. Silent fail — a misconfigured email backend will never crash the compliance system.
3. DRF support — works with React, Vue, mobile, and headless projects
# urls.py — use template views, API views, or both
urlpatterns = [
path('compliance/', include('compliance_shield.urls')), # template views
path('api/compliance/', include('compliance_shield.api_urls')), # DRF API views
]
Available API endpoints:
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/compliance/consent/ |
Current consent status |
| POST | /api/compliance/consent/grant/ |
Grant one or more consents |
| POST | /api/compliance/consent/withdraw/ |
Withdraw a consent |
| GET | /api/compliance/dsr/ |
List my DSRs |
| POST | /api/compliance/dsr/submit/ |
Submit a new DSR |
| GET | /api/compliance/access-log/ |
Sensitive data access log |
| GET | /api/compliance/retention/ |
View retention policies |
| GET | /api/compliance/jurisdiction/ |
Current jurisdiction info |
All endpoints require authentication. All return JSON.
Quick start
pip install django-compliance-shield
# settings.py
INSTALLED_APPS = [..., 'compliance_shield']
MIDDLEWARE = [
...
'django.contrib.auth.middleware.AuthenticationMiddleware',
'compliance_shield.middleware.ComplianceMiddleware',
...
]
COMPLIANCE_SHIELD = {
'ENCRYPTION_KEY_IN': env('ENCRYPTION_KEY_IN'),
'ENCRYPTION_KEY_US': env('ENCRYPTION_KEY_US'),
'ENCRYPTION_KEY_EU': env('ENCRYPTION_KEY_EU'),
'ENCRYPTION_KEY_OTHER': env('ENCRYPTION_KEY_OTHER'),
'BLIND_INDEX_SECRET_IN': env('BLIND_INDEX_SECRET_IN'),
'BLIND_INDEX_SECRET_OTHER': env('BLIND_INDEX_SECRET_OTHER'),
'PRIVACY_POLICY_VERSION': 'v1.0.0',
'DEFAULT_JURISDICTION': 'IN',
'REQUIRED_CONSENTS': ['data_collection', 'data_processing'],
}
# urls.py
urlpatterns = [
...
path('compliance/', include('compliance_shield.urls')),
]
python manage.py migrate
python manage.py compliance_setup
Wire consent into registration:
from compliance_shield.utils import record_registration_consents
record_registration_consents(user, request, jurisdiction='IN')
See docs/quickstart.md for the full guide.
Supported field types
pan ssn aadhaar passport gov_id dob financial health biometric custom
See docs/field-types.md for full reference.
Requirements
- Python 3.9+
- Django 3.2, 4.2, 5.0, 5.1, or 5.2+
cryptography>=41.0.0
Management commands
| Command | Description |
|---|---|
python manage.py compliance_setup |
Seed 29 retention policies |
python manage.py enforce_retention |
Enforce retention (run daily) |
python manage.py enforce_retention --dry-run |
Preview without changes |
python manage.py enforce_retention --jurisdiction IN |
Single jurisdiction |
python manage.py rotate_keys --model app.Model --field pan_number |
Rotate encryption key |
Author
Yogesh Chauhan — AI and Django engineer, Ahmedabad, India.
License
MIT — see LICENSE.
Release files for django-compliance-shield 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_compliance_shield-1.2.0.tar.gz | 66.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_compliance_shield-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 130.8 kB
Release files / django_compliance_shield-1.2.0.tar.gz
| Download URL | django_compliance_shield-1.2.0.tar.gz |
|---|---|
| Size | 66.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
72fa81fdc377f239998c54db8966283f734a7ebe22041e3f1536a2ad73427493
|
|
BLAKE2b-256 checksum How to use checksums |
c6748af630eafb1f6960bbf15ca6d56f842de5df682e456bd6c7f511fd04470f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|
Release files / django_compliance_shield-1.2.0-py3-none-any.whl
| Download URL | django_compliance_shield-1.2.0-py3-none-any.whl |
|---|---|
| Size | 64.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b06cc1f2f5f5fd8d9c15a2c7419b1389fde05efe146c1b7482c93b692437a43d
|
|
BLAKE2b-256 checksum How to use checksums |
c518ba838018007114fb40c03d550c5c4426e931d0b6b6339d362767f14588da
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|