Permissions and Access Control ============================== djxi provides a lightweight permission system that runs before any battery endpoint handler. It requires no external packages and integrates naturally with Django's ``request.user``. Quick start — requiring authentication --------------------------------------- Set ``requires_auth = True`` on the battery. Anonymous (unauthenticated) users receive a ``403 Forbidden`` response: .. code-block:: python from djxi import DXEndpointBattery, dx_get, dx_delete class SecureBattery(DXEndpointBattery): inline_template = "..." requires_auth = True @dx_get("data/", name="data") def data(self, request): return self.render_section(request, "data", {"rows": fetch_rows()}) Redirecting to a login page ---------------------------- Set ``login_url`` alongside ``requires_auth`` to redirect unauthenticated users instead of returning a 403: .. code-block:: python class SecureBattery(DXEndpointBattery): inline_template = "..." requires_auth = True login_url = "/accounts/login/" @dx_get("data/", name="data") def data(self, request): ... The redirect uses Django's ``django.shortcuts.redirect`` and returns a 302. Fine-grained control with ``check_permissions()`` -------------------------------------------------- Override ``check_permissions(self, request)`` for any logic beyond a simple authentication check. Return ``None`` to allow the request through or return any ``HttpResponse`` to short-circuit the handler: .. code-block:: python from django.http import HttpResponseForbidden class StaffOnlyBattery(DXEndpointBattery): inline_template = "..." def check_permissions(self, request): if not getattr(request.user, "is_staff", False): return HttpResponseForbidden("Staff only.") return None # allow through @dx_get("admin-data/", name="admin-data") def admin_data(self, request): ... Using Django's permission system: .. code-block:: python from django.http import HttpResponseForbidden class PermissionedBattery(DXEndpointBattery): inline_template = "..." REQUIRED_PERM = "myapp.change_item" def check_permissions(self, request): if not request.user.has_perm(self.REQUIRED_PERM): return HttpResponseForbidden() return None @dx_put("item//", name="update") def update(self, request, pk): ... How it works ------------ ``check_permissions()`` is defined on ``DXRouterMixin`` and called inside every view wrapper generated by ``url_patterns()`` — before the handler method runs. This applies to both sync and ``async def`` handlers. The default implementation in ``DXEndpointBattery`` honours ``requires_auth`` and ``login_url``; if you override it you can call ``super()`` to keep that behaviour and layer your own logic on top: .. code-block:: python class CombinedBattery(DXEndpointBattery): requires_auth = True login_url = "/login/" def check_permissions(self, request): # First, enforce authentication (from super) result = super().check_permissions(request) if result is not None: return result # Then, add a custom rule if request.user.profile.is_suspended: return HttpResponseForbidden("Account suspended.") return None Class attribute reference -------------------------- .. list-table:: :header-rows: 1 :widths: 25 10 65 * - Attribute - Default - Description * - ``requires_auth`` - ``False`` - When ``True``, unauthenticated users are blocked by the default ``check_permissions()`` implementation. * - ``login_url`` - ``None`` - URL to redirect unauthenticated users to. When ``None``, a ``403 Forbidden`` is returned instead. Method reference ---------------- .. py:method:: DXEndpointBattery.check_permissions(self, request) Called before every endpoint handler. :param request: The incoming ``HttpRequest``. :returns: ``None`` to allow the request through, or an ``HttpResponse`` to short-circuit the handler (e.g. ``HttpResponseForbidden()`` or ``redirect("/login/")``.