Models

Note

The code described here is in example/exampleapp/models.py.

Model classes

The example application is a simple CRUD application managing objects of two types: “parties” and “parcels” (the names are taken from the land tenure application we’re using django-tutelary for). Parties and parcels both belong to “projects” and projects belong to “organizations”. The application also manages user accounts (which are just User objects from django.contrib.auth.models) and django-tutelary policies. This is simple enough, but has enough complexity to illustrate how to use django-tutelary. (The application was actually used to help in the development of django-tutelary, since it seems nearly impossible to write something like this without an example to use it on!)

Registering actions

Each of the Organization and Project models register django-tutelary actions for listing entities and creating and deleting entities. Organizations are identified as organization/<org-name> and projects (which belong to organizations) are identified as project/<org-name>/<proj-name>. This demonstrates a useful feature of the path_fields member of the TutelaryMeta class: if the model field referred to by an entry in path_fields is a foreign key, then the path fields of the model referred to by the foreign key are inserted into the object components (i.e. the owning organization’s name appears in the object identifier for projects). The code to do this looks like this (for the Project model):

@permissioned_model
class Project(models.Model):
    name = models.CharField(max_length=100)
    organization = models.ForeignKey(Organization)

    class Meta:
        ordering = ('organization', 'name')

    class TutelaryMeta:
        perm_type = 'project'
        path_fields = ('organization', 'name')
        actions = [('project.list',
                    {'description': "Can list existing projects",
                     'permissions_object': 'organization'}),
                   ('project.create',
                    {'description': "Can create projects",
                     'permissions_object': 'organization'}),
                   ('project.delete',
                    {'description': "Can delete projects"})]

Each of the Party and Parcel models registers actions to list entities, view details of individual entities, and create, edit and delete individual entities. Here, the object identifiers contain the model primary key (represented by a pk entry in path_fields). Here’s the definition of the Parcel model:

@permissioned_model
class Parcel(models.Model):
    project = models.ForeignKey(Project)
    address = models.CharField(max_length=200)

    class Meta:
        ordering = ('project', 'address')

    class TutelaryMeta:
        perm_type = 'parcel'
        path_fields = ('project', 'pk')
        actions = [('parcel.list',
                    {'description': "Can list existing parcels",
                     'permissions_object': 'project'}),
                   ('parcel.create',
                    {'description': "Can create parcels",
                     'allow_get': True,
                     'permissions_object': 'project'}),
                   ('parcel.detail',
                    {'description': "Can view parcel details"}),
                   ('parcel.edit',
                    {'description': "Can update existing parcels",
                     'allow_get': True}),
                   ('parcel.delete',
                    {'description': "Can delete parcels",
                     'allow_get': True})]

    def get_absolute_url(self):
        return reverse('parcel-detail', kwargs={'pk': self.pk})

Decorators and metadata

Each of the models defined within the example application are decorated with the permissioned_model decorator from tutelary.decorators. To enable permissions for models defined in other Django applications, the permissioned_model function is used in a different way, calling it directly on the model class to be modified and passing the TutelaryMeta metadata as arguments. This can be seen in the way that tutelary.models.Policy and django.contrib.auth.models.User are both set up for permissioning. For example, the User model is set up for permissioning like this:

permissioned_model(
    User, perm_type='user', path_fields=['username'],
    actions=[
        ('user.list',   {'permissions_object': None}),
        ('user.create', {'permissions_object': None}),
        'user.detail',
        'user.edit',
        'user.delete'
    ]
)

“Free-floating” actions

In addition to the actions associated with models, it’s also possible to have “free-floating” actions that aren’t associated with any particular model or objects. These can be set up by calling Action.register, as is done for the “statistics” action here (Action is in tutelary.engine):

Action.register('statistics')

User policy assignments

The example application has one additional modelling component, which is to record the policies that we associate with individual users (using the UserPolicyAssignment model). This is application-specific, because we allow policies to contain variables (making them potentially more like policy templates) and the variables and their values are something we’d like to present in the user interface of the application, so we need to manage them explicitly. We also provide a set_user_policies function to wrap django-tutelary’s User.assign_policies functionality, making use of the user/policy assignment information that we’re recording.