Setup

The Restraint Configuration

Restraint is configured all in one place using the RESTRAINT_CONFIGURATION django setting. This should be a string path to a method that will return the fully qualified restraint config.

An example Restraint configuration is provided below. Details of the configuration are outlined in later sections.

RESTRAINT_CONFIGURATION = 'app.permissions.get_restraint_config'

def get_restraint_config():
    return {
        'perm_set_getter': perm_set_getter_function,
        'perm_checker': perm_checker_function,
        'perm_sets': {
            'super': {
                'display_name': 'Super',
                'locked': True,
                'hidden': True
            },
            'individual': {
                'display_name': 'Individual',
            },
            'staff': {
                'display_name': 'Staff'
            }
        },
        'perms': {
            'can_edit_stuff': {
                'display_name': 'Can Edit Stuff',
                'levels': {
                    'all_stuff': {
                        'display_name': 'All Stuff',
                        'id_filter': None,
                    },
                    'some_stuff': {
                        'display_name': 'Some Stuff',
                        'id_filter': lambda a: User.objects.filter(id=a.id).values_list('id', flat=True),
                    },
                    'only_superusers': {
                        'display_name': 'Only Superusers',
                        'id_filter': lambda a: User.objects.filter(is_superuser=True).values_list('id', flat=True),
                    },
                },
                'locked': True
            },
            'can_view_stuff': {
                'display_name': 'Can View Stuff',
                'levels': constants.BOOLEAN_LEVELS_CONFIG,
            }
        },
        'default_access': {
            'super': {
                'can_edit_stuff': ['all_stuff', 'some_stuff'],
                'can_view_stuff': [constants.BOOLEAN_LEVELS_NAME],
            },
            'individual': {
                'can_edit_stuff': ['some_stuff'],
            },
            'staff': {
                'can_edit_stuff': ['some_stuff', 'only_superusers']
            }
        }
    }

Defining The Permission Set Getter

The perm_set_getter key in the configuration points to a function that takes a user object. This function is responsible for returning a list of all perm sets that are associated with that user object. For example,

def perm_set_getter(user):
    perm_sets = ['individual']
    if u.is_superuser:
        perm_sets.append('super')
    if u.is_staff:
        perm_sets.append('staff')
    return perm_sets

In this example, the user object is a Django User model, and the perm_set_getter function returns individual, super, or staff permission sets based on the contents of the user object.

Defining The Permission Checker

This is optional if you do not need any extra checks.

The perm_checker key in the configuration points to a function that allows a custom check of permissions.

def custom_permission_checker(user, user_permissions, permission, level):
    if user.is_superuser:
        return True
    return False

Defining Permission Sets

The perm_sets key is responsible for defining all of the permission sets of your application. These must correlate directly with what perm_set_getter may return.

In the configuration from above, the user has defined that the permission sets are super, individual, and staff. Along with this, human-readable display names are also configured in the dictionary.

Defining Permissions And Their Levels

The perms key is responsible for defining all of the permissions and their associated levels. Each top-level key in the perms config defines the permission name, and the dictionary for each permission defines the display name of the permission and the levels that are associated with that permission.

In the above example, the can_edit_stuff permission is defined over three levels. Each of these levels defines a callable id_filter function that can take the user and return lists of IDs associated with the querysets that should be restricted. For example, the some_stuff level only allows the user to edit stuff that belongs to their account ID while the only_superusers level allows one to edit the stuff belonging to super users. The all_stuff level has no id_filter, so it provides access over the entire queryset.

Note that if a user has been granted multiple permission levels over the same permission, the results of those levels will be unioned together.

If a permission is Boolean and has no levels, it must be configured with the BOOLEAN_LEVELS_CONFIG object provided in the constants module of Restraint.

Defining Default Permission Set Access

The Restraint configuration also allows the user to provide the default access levels for all permission sets. This prevents the user from having to write data migrations or initial fixtures to populate their permissions.

For example, the above configuration allows super users to edit all stuff or any stuff and also provides them access to view stuff. The above configuration only allows individual users to edit some stuff without being able to view stuff.

Syncing Your Configuration To The Database

The Restraint configuration will need to be synced to the database before it can be used by an application. Similar to Django’s update_permissions, Restraint provides an update_restraint_db management command. When this command is called, all permission sets and permission levels are synced. Any permission sets and levels that were in the configuration before and not in the current one will be deleted.

The default_access configuration in the Restraint configuration will only be synced the first time this management command is executed. This behavior can be overridden by passing the --flush_default_access parameter to the management command.

How Do I Add Permissions To Individuals?

Adding permissions to individuals is not supported in the setup methods of Restraint. However, this may be done dynamically with model manager methods that are covered in the Usage documentation.