Skip to main content

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+

Any user model works, including custom AUTH_USER_MODEL classes. The Cedar principal type is your user model's class name (e.g. Member::"5" for a Member model; User::"5" with Django's default model). The is_staff and is_superuser attributes and Group parent entities are included when your model provides them — a bare AbstractBaseUser model gets just id plus any provider-supplied attributes.

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<UserModel>::"<pk>" for authenticated users, where <UserModel> is your user model's class name via get_user_model() (User::"<pk>" with Django's default user model), or Anonymous::"guest" otherwise. The id attribute is always included; is_staff and is_superuser attributes and Group::"<name>" parent entities from your model's groups relation are included when your model provides them, so principal in Group::"editors" works out of the box for models that have one. Policies — including forbid policies — that reference an attribute your model doesn't provide never apply.
  • actionAction::"<name>" from the view's action_names mapping (HTTP method → action name). HEAD requests are authorized using the view's "GET" action mapping; a "HEAD" key in action_names is 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 against self.get_object().
  • CurrentUserScopedMixin — authorize against the current user and filter the queryset to user=<request.user>.
  • AsyncLoginRequiredMixin — a LoginRequiredMixin that works on async views. CedarAuthorizationMixin itself 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_cedar-0.2.0.tar.gz (13.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_cedar-0.2.0-py3-none-any.whl (16.0 kB view details)

Uploaded Python 3

File details

Details for the file django_cedar-0.2.0.tar.gz.

File metadata

  • Download URL: django_cedar-0.2.0.tar.gz
  • Upload date:
  • Size: 13.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for django_cedar-0.2.0.tar.gz
Algorithm Hash digest
SHA256 22d6b985bde30f346d231fa5e7a2db94d4c52c996f30fd6d5afddba4eb0d87a1
MD5 683a2acf8200257f8a1c5f3e0045c3dc
BLAKE2b-256 282ae218613e90f544fd492fa3da699b598420b1736d21ee6fb7f324342a768a

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_cedar-0.2.0.tar.gz:

Publisher: publish.yml on hyperscale-consulting/django-cedar

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_cedar-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: django_cedar-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 16.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for django_cedar-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8b2508616e8ce1ce116739bf73d7ed5bf8a1aab97106465d94757b84bb57a62b
MD5 13cc4ae75da15af6b8f42757ebc971a9
BLAKE2b-256 7239227c370ae1ea2fa167f6d5ed8ef0501d0e22fdf1685e4e71a45565fd2687

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_cedar-0.2.0-py3-none-any.whl:

Publisher: publish.yml on hyperscale-consulting/django-cedar

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page