Overview

As explained before capabilities permission system provide access to objects only based on a . This element provide: an identifier (uuid), a set of allowed actions to users (aka Capability), and eventually an expiration date.

This figure shows how is it implemented in Django-Caps:

../_images/caps-models.drawio.png

The Object is accessed through its Share. A Share is assigned to an Agent which identifies the user. The Share provides capabilities which are linked to Django’s auth Permission.

Django-Caps provides views that will use the provided scheme in order to grant user access or actions.

, , models are abstract. When Object is subclassed in a concrete model, a concrete Share is generated (accessible from subclass scope). The same occurs between Share and Capability. This mechanism ensure different things:

  • The Share and Capability models are associated to one object type, allowing reverse relations to be accessible (comparing for example over a solution using ContentType framework);

  • This ensure clear segregation for Shares and capabilities per object type and reduce tables sizes;

  • We can exploit this mechanism for eg. default Share capabilities;

Share

As told, a Share provides a set of capabilities. Each capability grants two things: permission (access or action) and this permission to be share (or not).

A Share can be shared, which means that a new one will be created based one the current one. This creates a chains of parent-child Shares, ensuring control over the access. In Django-Caps, this process is called derivation, as a Share is derived from another one.

The first Share of this chain is the root Share. There only can be one Share for each object, which is owned by a single agent.

# Taking the example from index page
from caps.models import Agent
from .models import Post

agent = Agent.objects.all()[0]
agent_2 = Agent.objects.all()[2]

# this raises ValueError: only one Share is allowed
post = Post.objects.filter(Shares__isnull=False).first()
access = Post.Share.create_root(agent, post)

The root Share will use the default capabilities as initial ones.

Capability

A Capability represent a single action or access to be granted. It also can grant sharing this permission. It can in some way be looked as a through table of a Django’s ManyToManyField although it not implemented as is (due to technical reasons).

A default capability is the one provided by default when creating a root Share. It is simply a capability without an assigned Share. Since a Capability table is created for each Object concrete sub-model, we are sure they will only target this sub-model.

When user derives a Share, eg for allowing Alice to access the object, he can provide her the ability to reshare it using provide the maximum amount of derivation as an absolute value relative to root. Each time a Share is derived, the max_derive is decremented by one. This implies that:

..code-block:: python

access = Share.objects.all().first() capability = access.capabilities.all().first()

if capability.max_derive == 1:

# this means that derived capability can’t be reshared assert capability.derive().max_derive == 0

elif capability.max_derive == 0:

# this raises PermissionDenied, as capability can’t be derived capability.derive()

else:

# this means that derived capability can’t be shared assert not capability.derive(0).can_derive()

# this means that derived capability can be reshared, as max_derive > 1 assert capability.derive(1).can_derive()