# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['drf_triad_permissions']

package_data = \
{'': ['*'], 'drf_triad_permissions': ['templates/drf_triad_permissions/*']}

install_requires = \
['django>=2.0,<4.0', 'djangorestframework>=3.11.0,<4.0.0']

setup_kwargs = {
    'name': 'drf-triad-permissions',
    'version': '0.2.0',
    'description': 'Django Rest Framework viewset permissions through triads',
    'long_description': 'drf-triad-permissions\n=====================\n\n.. image:: https://img.shields.io/badge/packaging-poetry-purple.svg\n    :alt: Packaging: poetry\n    :target: https://github.com/sdispater/poetry\n\n.. image:: https://img.shields.io/badge/code%20style-black-black.svg\n    :alt: Code style: black\n    :target: https://github.com/ambv/black\n\n.. image:: https://badges.gitter.im/Join%20Chat.svg\n    :alt: Join the chat at https://gitter.im/drf-triad-permissions\n    :target: https://gitter.im/drf-triad-permissions/community?utm_source=share-link&utm_medium=link&utm_campaign=share-link\n\n.. image:: https://github.com/lorinkoz/drf-triad-permissions/workflows/code/badge.svg\n    :alt: Build status\n    :target: https://github.com/lorinkoz/drf-triad-permissions/actions\n\n.. image:: https://coveralls.io/repos/github/lorinkoz/drf-triad-permissions/badge.svg?branch=master\n    :alt: Code coverage\n    :target: https://coveralls.io/github/lorinkoz/drf-triad-permissions?branch=master\n\n.. image:: https://badge.fury.io/py/drf-triad-permissions.svg\n    :alt: PyPi version\n    :target: http://badge.fury.io/py/drf-triad-permissions\n\n.. image:: https://pepy.tech/badge/drf-triad-permissions/month\n    :alt: Downloads\n    :target: https://pepy.tech/project/drf-triad-permissions/month\n\n|\n\nDjango Rest Framework permissions are a powerful tool for limiting user access to views and viewsets.\nHowever, it\'s not always easy or clean to set up a comprehensive permission system that can depend on multiple factors,\nsuch as the user coming in the request, the parameters of the matched URL, or the specificities of the object being\nrequested by the client.\n\n``drf-triad-permissions`` is one of the many approaches for configuring permissions in medium to large scale projects,\nwith elegance and reusability in mind. It bases the permission system on string triads that can be used both as\npermission statements as well as permission expectations.\n\nTriads\n------\n\nTriads are strings that can be split in three parts, usually ``resource::object::action``. The substring ``::`` acts\nas separator of the parts. Each level can be attached any meaning, but the original intention is to consider that\nthe first level represents a resource, the second an object, and the third level an action.\n\nFor instance, the triad ``polls::all::read`` can be read as "user has access to read all polls". Another example:\n``payment::owner:4368::reverse`` can be read as "user has access to reverse payments that match \'owner:4368\'". The\n\'owner:4368\' part can have any meaning, in this context, it could signify that 4368 is a user ID, whereas owner means\nthat the payment is owned by that specific user.\n\nTriads as statements\n++++++++++++++++++++\n\nTriads can be used as statements for specifying which permissions a user has. For instance, a user can have this set of\ntriads attached to their permissions::\n\n    payments::all::read\n    payments::from:john@doe.com::all\n    payments::year:2020::review\n\nThis means that the user can read all payments, do anything with payments from john at doe.com (hopefully himself) and\nreview all payments from year 2020.\n\nTriads as expectations\n++++++++++++++++++++++\n\nThe true power of triads come when they are used to define expectations. Suppose we have a DRF ``ModelViewSet`` that\ncontrols the payments. In a model viewset, it\'s expected that users can "list", "create", "update" and "delete" items.\nAdditional actions can be defined, such as "review". In this case, this set of triads could be used as expectation::\n\n    payments::all::{action}\n    payments::from:{obj.author.email}::{action}\n    payments::year:{url.year}::{action}\n\nExpectation triads are assumed to be disjunctive. That is, a user would only need to match ONE of those to be allowed\nto perform the action they are intending.\n\nNotice the use of placeholders. Here, triads are relying on certain elements to be interpolated at the actual time of\nchecking. Placeholders will depend on the payment object being requested (``obj``) and on the year parameter of the URL\nthat matched the current viewset (``url``). The ``action`` placeholder will be converted to the action provided by DRF\nviewsets, such as ``list``, ``update``, ``partial-update`` or ``delete``.\n\nLet\'s suppose a user with the triad statements we saw before attempts to do a PUT request against the (fake) URL\nhttps://my.domain.com/api/payments-from/2019/10802/. This URL is handled by ``PaymentsViewSet`` whose expectations are\ndefined in the set of triads we just saw. Since this is a PUT request against a detail endpoint, it\'s going to get\nhandled as an "update" action. Let\'s just assume that the payment 10802 of year 2019 has ``author.email`` equal to \njohn at doe.com. When DRF\'s permission machinery checks the permissions of the requesting user against the expected\nstatements, these are the concrete checks that will be used to test against the user::\n\n    payments::all::update\n    payments::from:john@doe.com::update\n    payments::year:2019::update\n\nAnd let\'s say that the expectation ``payments::from:john@doe.com::update`` will match with the permission\n``payments::from:john@doe.com::all``.\n\nTriad matching\n++++++++++++++\n\nTriad matching is done by level, with some simplistic rules.\n\n#. Two identical strings always match.\n#. The strings ``all`` and ``*`` will match with anything.\n#. The string ``read`` will match with ``head``, ``options``, ``get``, ``list`` and ``retrieve``.\n#. The string ``write`` will match with ``post``, ``put``, ``patch``, ``delete``, ``create``, ``update``,\n   ``partial-update`` and ``destroy``.\n\nIt\'s important to note that matching does not occurr in both directions. If the viewset is expecting ``list`` and the\nuser has ``all`` the matching succeeds, but the opposite will not.\n\nSo, in the example::\n\n    user permission     -> payments::from:john@doe.com::all\n    viewset expectation -> payments::from:john@doe.com::update\n\nThe first and second level will match by rule 1, and the third level will match by rule 2.\n\nPolicies\n--------\n\nTriads can be grouped in policies for easy reutilization. This package comes with a pre-defined basic policy:\n\n.. code-block:: python\n\n    class BasicPolicy(Policy):\n        default = [\n            "{resource}::all::{action}",\n            "{resource}::new::create",\n            "{resource}::id:{obj.id}::{action}",\n        ]\n\nThis policy has the following meaning:\n\n* User must have permission to perform the action on all objects.\n* User must have permission to create a new resource (``new`` acts as syntactic sugar here, remember that there is no\n  implicit meaning attached to each level).\n* User must have permission to perform the action on the specific object, matching by id.\n\nPolicies can be used as DRF viewset permissions like this:\n\n.. code-block:: python\n\n    class PaymentsViewSet(ModelViewSet):\n        queryset = Payment.objects.all()\n        serializer = PaymentSerializer\n        permission_classes = BasicPolicy.expand()\n\nPolicies are the recommended way of using triad permissions. However, if you need to create a permission class on the\nfly, you can use ``drf_triad_permissions.permissions.get_triad_permission``. This function has the same parameters than\nthe policy class variables, which will be explained in the next section.\n\nParameters\n++++++++++\n\nPolicies can be created with the following class variables: ``default``, ``read``, ``write``, plus all HTTP verbs in\nlower case (e.g. ``post``, ``get``), plus all viewset actions in lower case (e.g. ``retrieve``, ``partial_update``,\n``review``). Each class variable accepts a list of triads that will be evaluated disjunctively, that is, with OR.\nFor instance, a read-only policy can be created with:\n\n.. code-block:: python\n\n    from drf_triad_permissions import Policy\n\n    class ReadOnlyPolicy(Policy):\n        read = [\n            "{resource}::all::{action}",\n            "{resource}::id:{obj.id}::{action}",\n        ]\n        write = []\n\nNotice how the ``write`` parameter needs to be explicitly stated as an empty list.\n\nIn the absence of any specific parameter, ``default`` will be always used, which defaults to an empty list.\n\nThis example of read-only policy can also be created on the fly by calling:\n\n.. code-block:: python\n\n    from drf_triad_permissions import get_triad_permission\n\n    get_triad_permission(\n        read=[\n            "{resource}::all::{action}",\n            "{resource}::id:{obj.id}::{action}",\n        ],\n        write=[],\n    )\n\nAs final example, if you wanted to limit the basic policy to exclude deletions, you would do this:\n\n.. code-block:: python\n\n    from drf_triad_permissions import BasicPolicy\n\n    class BasicPolicyWithNoDeletions(BasicPolicy):\n        destroy = []\n\nContributing\n------------\n\n* Join the discussion at https://gitter.im/drf-triad-permissions/community.\n* PRs are welcome! If you have questions or comments, please use the link above.\n* To run the test suite run ``make`` or ``make coverage``. The tests for this project live inside a small django\n  project called ``triads_sandbox``.\n',
    'author': 'Lorenzo Peña',
    'author_email': 'lorinkoz@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/lorinkoz/drf-triad-permissions',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.6,<4.0',
}


setup(**setup_kwargs)
