Skip to content

11. Access as code

The bundles and roles a project always has can live in a file, reviewed like code and the same on every machine. The shop's:

"""
Who may do what in the shop, as code - applied by ``clearance_sync``, and by
every ``migrate`` (``SYNC_ON_MIGRATE``).

The file owns what it names: the admin site shows these bundles and roles
read only. Who is given a bundle or a role is not the file's to say - that is
done in the admin site, or by ``python manage.py example_data``.
"""

ACCESS = {
    'bundles': {
        # The orders of their own branch: read, edited and approved - never the price.
        'Branch managers': {
            'shop.order_view': 'branch',
            'shop.order_edit': 'branch',
            'shop.order_approve': 'branch',
            'shop.order_print_invoice': 'branch',
        },
        # Every order, with its price, and nothing changed.
        'Accountants': {
            'shop.order_view': 'all',
            'shop.order_view_price': None,
            'shop.order_print_invoice': 'all',
        },
        # Their own orders: read, and new ones placed for themselves.
        'Customers': {
            'shop.order_view': 'own',
            'shop.order_create': 'own',
        },
    },
    'roles': {
        # A customer starts with "Customers", and can never hold more than this.
        'customer': {
            'name': 'Customer',
            'ceiling': ['shop.order_view', 'shop.order_create'],
            'bundles': ['Customers'],
        },
        'manager': {
            'name': 'Branch manager',
            'ceiling': None,
            'bundles': ['Branch managers'],
        },
    },
}

The settings name it, and every migrate applies it:

CLEARANCE = {'ACCESS': 'config.access.ACCESS', 'SYNC_ON_MIGRATE': True}

Or by hand:

python manage.py clearance_sync --dry-run    # what would change, writing nothing
python manage.py clearance_sync --check      # in CI: fail when the database differs
python manage.py clearance_sync              # apply it, and say what changed

Who owns what

The file owns what it names, and nothing else. A bundle or a role the file names is shown read only in the admin site, with a note saying so - see the role page in step 10. A bundle an administrator makes is theirs, and the file never touches it.

Who is given a bundle or a role is not the file's to say. That stays in the admin site - or, in the shop, in example_data.

A status action of a model that keeps Django's permissions goes in the file too, under status_actions:

ACCESS = {
    'status_actions': {
        'shop.Payment': {'refund': {'field': 'state', 'to': ['refunded'], 'from': ['paid']}},
    },
}

Start-up refuses a file naming something that cannot be - a permission not written app_label.codename, a model not installed, a field or a value its model does not have (E001).