Cedar policy-based authorization for Django
Project description
django-cedar
Cedar policy-based authorization for Django.
Cedar is an open-source policy language for expressive, analyzable permissions. django-cedar lets you express who can do what in Cedar policy files instead of scattering permission logic through your views, and enforces those policies with class-based-view mixins.
Requirements
- Python 3.12+
- Django 5.2+
Your user model must subclass
django.contrib.auth.models.AbstractUser. The Cedar principal type is always
User, regardless of the model's class name.
Installation
pip install django-cedar
Add the app (optional, but recommended — it enables startup-time configuration checks via Django's system check framework):
INSTALLED_APPS = [
# ...
"django_cedar",
]
Quickstart
1. Write a policy file (policies.cedar next to manage.py):
// Staff can do anything
permit(principal, action, resource)
when { principal.is_staff };
// Anyone signed in can view widgets
permit(principal is User, action == Action::"ViewWidget", resource);
2. Point Django at it:
CEDAR_POLICY_PATH = "policies.cedar" # relative paths resolve against BASE_DIR
3. Enforce it in your views:
from django_cedar.views import AuthorizedDetailView
from .models import Widget
class WidgetDetailView(AuthorizedDetailView):
model = Widget
action_names = {"GET": "ViewWidget"}
Every request is authorized in dispatch(). The Cedar request is built as:
- principal —
User::"<pk>"for authenticated users,Anonymous::"guest"otherwise. User entities carryid,is_staffandis_superuserattributes, and the user's Django groups become parent entities (Group::"<name>"), soprincipal in Group::"editors"works out of the box. - action —
Action::"<name>"from the view'saction_namesmapping (HTTP method → action name). HEAD requests are authorized using the view's"GET"action mapping; a"HEAD"key inaction_namesis not consulted. - resource — the object returned by the view's
get_resource()hook, as<ModelClass>::"<pk>";System::"global"when there is no resource.
Denied requests raise django.core.exceptions.PermissionDenied (HTTP 403).
Views and mixins
| Class | Resource used for the check |
|---|---|
AuthorizedDetailView / AuthorizedUpdateView / AuthorizedDeleteView |
self.get_object() |
AuthorizedListView / AuthorizedCreateView / AuthorizedTemplateView / AuthorizedFormView |
System::"global" (override get_resource()) |
Compose the behavior yourself with CedarAuthorizationMixin plus:
ResourceIsCurrentObjectMixin— authorize againstself.get_object().CurrentUserScopedMixin— authorize against the current user and filter the queryset touser=<request.user>.AsyncLoginRequiredMixin— aLoginRequiredMixinthat works on async views.CedarAuthorizationMixinitself supports async views too.
Custom scoping is one method:
class ProjectScopedView(CedarAuthorizationMixin, ListView):
action_names = {"GET": "ListTasks"}
def get_resource(self, request):
return Project.objects.get(pk=self.kwargs["project_pk"])
Exposing model attributes to policies
Models opt in to exposing attributes with authz_fields(), and pull related
entities into the request with authz_related_entities():
class Task(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
status = models.CharField(max_length=20)
def authz_fields(self):
return {"status": self.status, "project": str(self.project_id)}
def authz_related_entities(self):
return [self.project]
permit(principal, action == Action::"CloseTask", resource is Task)
when { resource.status == "open" };
Settings
| Setting | Required | Description |
|---|---|---|
CEDAR_POLICY_PATH |
yes | Path to the Cedar policy file. Relative paths resolve against BASE_DIR. |
CEDAR_PRINCIPAL_ATTRIBUTE_PROVIDERS |
no | List of dotted paths to classes with get_attributes(user) -> dict (extra principal attributes) and optionally get_entities(user) -> iterable[Entity] (extra entities). |
CEDAR_CONTEXT_PROVIDERS |
no | List of dotted paths to classes with get_context(user, action, resource) -> dict. Results are deep-merged into the Cedar request context in list order; a per-call context= argument merges last and wins. |
Example context provider:
class FeatureFlagContext:
def get_context(self, user, action, resource):
return {"allow": {"self_signup": settings.ALLOW_SELF_SIGNUP}}
Using the engine directly
from django_cedar import create_authz
authz = create_authz() # loads policies + providers from settings (cached)
authz.authorize(request.user, "ExportReport", report) # raises PermissionDenied on deny
System checks
With django_cedar in INSTALLED_APPS, manage.py check (and every server
start) verifies that CEDAR_POLICY_PATH is set and the file exists
(django_cedar.E001/E002), that it parses as Cedar (E003), that the
provider settings are lists or tuples (E006), and that all configured
providers import and have the right methods (E004/E005).
License
Apache-2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_cedar-0.1.0.tar.gz.
File metadata
- Download URL: django_cedar-0.1.0.tar.gz
- Upload date:
- Size: 13.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
67092340c6ce7a9dbaa16d14c0d2489412401c21715f33cc6a53bc4dba1a41b1
|
|
| MD5 |
102926910f14a0964501f301faabb221
|
|
| BLAKE2b-256 |
3dd29c54206f48c4fa4fc601072f9a111a3f89d88a9546034a85b6addcdb88ca
|
Provenance
The following attestation bundles were made for django_cedar-0.1.0.tar.gz:
Publisher:
publish.yml on hyperscale-consulting/django-cedar
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_cedar-0.1.0.tar.gz -
Subject digest:
67092340c6ce7a9dbaa16d14c0d2489412401c21715f33cc6a53bc4dba1a41b1 - Sigstore transparency entry: 2144459688
- Sigstore integration time:
-
Permalink:
hyperscale-consulting/django-cedar@d558634a3c07c52628a8f1c53d086f782519c3f2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/hyperscale-consulting
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d558634a3c07c52628a8f1c53d086f782519c3f2 -
Trigger Event:
release
-
Statement type:
File details
Details for the file django_cedar-0.1.0-py3-none-any.whl.
File metadata
- Download URL: django_cedar-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2bb928f4de0dd1c7fde78948b64f092bf1ea377895d8220b8e5e0982a16cb0c3
|
|
| MD5 |
e80dc20034c6d84189d5aba7c47882e7
|
|
| BLAKE2b-256 |
b42419920e9351c66366fd64a06e058fca17176f9dfe880de4749bcd5c840056
|
Provenance
The following attestation bundles were made for django_cedar-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on hyperscale-consulting/django-cedar
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_cedar-0.1.0-py3-none-any.whl -
Subject digest:
2bb928f4de0dd1c7fde78948b64f092bf1ea377895d8220b8e5e0982a16cb0c3 - Sigstore transparency entry: 2144459698
- Sigstore integration time:
-
Permalink:
hyperscale-consulting/django-cedar@d558634a3c07c52628a8f1c53d086f782519c3f2 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/hyperscale-consulting
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d558634a3c07c52628a8f1c53d086f782519c3f2 -
Trigger Event:
release
-
Statement type: