Permissions
=============
Goal
----
Scope which rows a user can reach and which actions they may perform. Rules
registered in ``djcrud.py`` with :func:`~djcrud.permissions.add_perm` are **shared** across
every CRUD surface: **HTML** pages, the **API** (DRF ViewSets), and **MCP**
agents that proxy Bearer calls to ``/api/``.
The ``djcrud_example.security_example`` app demonstrates this on a single
``Document`` model at ``/secured-document/``.
Expected behavior
-----------------
.. list-table::
:header-rows: 1
:widths: 25 15 15 20 25
* - User
- list/detail
- create
- update/delete own
- update/delete others
* - Anonymous
- all rows visible
- denied
- denied
- denied
* - Authenticated
- all rows visible
- allowed
- allowed
- denied
Permission registry
-------------------
:meth:`~djcrud.Site.build` imports every ``djcrud.py`` module. Register rules,
then append the router:
.. literalinclude:: ../../src/djcrud_example/security_example/djcrud.py
* ``view`` — everyone (``lambda …: True``); ``add_queryset`` hides drafts from
strangers (published rows only, unless owner or superuser).
* ``add`` — authenticated users only.
* ``change`` / ``delete`` — owner or superuser (``secured_document_change``).
* ``publish`` — owner of a draft only (``can_publish``).
Try it
------
Log in as two users (see :doc:`../install`), create documents with different
owners, and visit
`http://localhost:8000/secured-document/ `_.
For **Publish**, open a draft you own at
`http://localhost:8000/secured-document// `_.
.. figure:: /_static/screenshots/publish-action-menu.png
:alt: Object menu with Publish action on a draft document
:align: center
:width: 90%
.. figure:: /_static/screenshots/publish-action-success.png
:alt: Document detail after publishing
:align: center
:width: 90%
Tests
-----
* `tests/test_security_example.py `_
Next: :doc:`drf` adds an optional JSON API; :doc:`spa` adds the SPA shell and
client codegen.