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.