caps.models

caps.models.agent

class Agent(*args, **kwargs)

Bases: Model

An agent is the one executing an action. It can either be related to a specific user (anonymous included) or group.

A user can impersonate multiple agent.

exception DoesNotExist

Bases: ObjectDoesNotExist

exception MultipleObjectsReturned

Bases: MultipleObjectsReturned

clean()

Hook for doing any extra model-wide validation after clean() has been called on every field by self.clean_fields. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS.

concreteobjectShare_set

Accessor to the related objects manager on the reverse side of a many-to-one relation.

In the example:

class Child(Model):
    parent = ForeignKey(Parent, related_name='children')

Parent.children is a ReverseManyToOneDescriptor instance.

Most of the implementation is delegated to a dynamically defined manager class built by create_forward_many_to_many_manager() defined below.

group

Agent targets this group.

group_id
id

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

property is_anonymous: bool

Return True when Agent targets anonymous users.

is_default

If True, use this Agent as user’s default.

objects = <django.db.models.manager.ManagerFromAgentQuerySet object>
access

Public Share to agent.

user

Agent targets this user.

user_id
class AgentQuerySet(model=None, query=None, using=None, hints=None)

Bases: QuerySet

group(group)

Filter by group.

Return type:

Parameters:

group (Group)

user(user, strict=False)

Filter by user or its groups.

Parameters:
  • user (User) – User

  • strict (bool) – if False, search on user’s groups.

Return type:

caps.models.capability

CanOne: TypeAlias = django.contrib.auth.models.Permission | int | tuple[str, int | django.contrib.contenttypes.models.ContentType | type]

Describe lookup for a capability’s permission.

It either can be:

  • A Permission or a permission id;

  • A tuple with Permission codename, and content type (id, ContentType instance, or model class);

It is used by

CanMany: TypeAlias = django.contrib.auth.models.Permission | int | tuple[str, int | django.contrib.contenttypes.models.ContentType | type] | collections.abc.Iterable[django.contrib.auth.models.Permission | int | tuple[str, int | django.contrib.contenttypes.models.ContentType | type]]

Describe lookup value for multiple capabilities’ description.

It is used by

class Capability(*args, **kwargs)

Bases: Model

A single capability providing permission for executing a single action.

It is linked to an object by a . The Share is the entry point for user to address/access the object. The capability represent what he can do.

This model is provided as abstract model whose implementation MUST provide a Share foreign key to a (reverse relation: capabilities). The foreign key is nullable, None have special meaning: it represents default assigned capabilities to newly created root Share instances. They can be fetched using CapabilityQuerySet.get_initials().

See documentation for more information.

It is recommanded to select_related permission in order to read name and codename.

Derivation

Capability can derived: it means the permission is shared to another agent. To be allowed to share, must be greater than 0. When sharing only a lower value is allowed to the new capability’s field.

Lets see what it does:

from models import MyObject

permission = Permission.objects.all().first()
cap = MyObject.Capability(permission=permission, max_derive=2)
#  `cap` is not saved, we don't core to provide a Share for this example.

# providing no max_derive defaults to 0
cap_1 = cap.derive(0)
assert cap_1.max_derive == 0

# this raises PermissionDenied
cap_1.derive()

# max_derive reduced by 1
cap_1 = cap.derive()
assert cap_1.max_derive == 1

cap_2 = cap_1.derive()
assert cap_2.max_derive == 0
class Meta

Bases: object

abstract = False
unique_together = (('Share', 'permission_id'),)
can_derive(max_derive=None)

Return True if this capability can be derived.

Return type:

bool

Parameters:

max_derive (None | int)

derive(max_derive=None, Share=None, **kwargs)

Derive a new capability from self (without checking existence in database).

Return type:

Parameters:
  • max_derive (None | int) – when value is None, it will based the value on self’s py:attr:max_derive minus 1.

  • **kwargs

    extra initial argument of the new Capability

:return the new unsaved Capability.

Yield PermissionDenied:

when Capability derivation is not allowed.

Parameters:

max_derive (None | int)

Return type:

classmethod get_object_class()

Return related Object class.

classmethod get_Share_class()

Return related Share class.

is_derived(capability=None)

Return True if capability is derived from this one.

Return type:

bool

Parameters:

capability ()

max_derive

Maximum allowed derivations.

objects
permission

Related permission

permission_id
class CapabilityQuerySet(model=None, query=None, using=None, hints=None)

Bases: QuerySet

Queryset and manager used by Capability models.

can(permissions)

Filter using provided permission.

When permission is an iterable, it will perform an OR conditional statement between all provided values.

Parameters:

permissions (CanMany) – permissions to filter.

Return type:

static can_one_lookup(permission, paccessix='permission__')

Return lookup for one permission.

Return type:

dict[str, str | int]

Parameters:
  • permission (CanOne) – to get lookup for.

  • paccessix – paccessix keys with this value.

:return lookup argument dictionnary.

Yield ValueError:

on unsupported permission types.

Parameters:

permission (CanOne)

Return type:

dict[str, str | int]

initials()

Filter capabilities used as initial values of a Share.

caps.models.capability_set

class CapabilitySet

Bases: object

Base class to handle set of capabilities.

This class should not be used per se.

Cap: int | tuple[int, int]

Capability information, as tuple of (permission_id, max_derive) or single permission id (then max_derive is None).

Capability: type[Capability] | Permission

Capability class to use. It must be set in order to use CapabilitySet.

Caps: Iterable[CapabilitySet.Cap]

Many capability information.

capabilities: Iterable[Capability] = None

Capabilities contained in the CapabilitySet.

create_capabilities(caps, **kwargs)

Create multiple capabilities based on descriptors.

Parameters:
  • caps – capabilities’ informations

  • **kwargs

    extra initial arguments

Return type:

list[CapabilitySet.Capability]

:return a list of unsaved capabilities instances.

create_capability(cap, **kwargs)

Create a single (unsaved) capability.

Parameters:
  • cap – capability information

  • **kwargs

    extra initial arguments

Return type:

CapabilitySet.Capability

:return the new unsaved capability instance.

derive_caps(caps=None, raises=False, defaults={})

Derive capabilities from this set.

When caps is provided, it will only derive allowed derivations for declared capabilities.

When caps is not provided, it derives all allowed capabilities of the set.

The created capabilities are not saved to the db.

Parameters:
  • source – capabilities to derive

  • caps – specify which capabilities to derive

  • raises – if True, raise exception when there are denied derivation.

  • defaults – initial arguments to pass to all generated capabilities.

Return type:

list[]

:return the list of derived capabilities. :yield PermissionDenied: when there are unauthorized derivation and :py:param:`raises` is True.

get_capabilities()
Return type:

Iterable[]

get_capability(codename)

Get capability by name or None.

Return type:

| None

Parameters:

codename (str)

get_capability_kwargs(cap, defaults)

From provided cap description, return a tuple with permission id and Capability.derive() arguments.

We return a tuple because permission id can not be provided to the derive method.

Parameters:
  • cap – the capability descriptor;

  • **kwargs

    extra arguments to pass down to the method;

  • defaults (dict[str, Any])

Return type:

tuple[int, dict[str, Any]]

is_derived(other)

Return True if capabilities iterable is a subset of self.

Set is a subset of another one if and only if: - all capabilities of subset are in set and derived from set (cf. Capability.is_subset) - there is no capability inside subset that are not in set.

Return type:

bool

Parameters:

other ()

caps.models.Share

class Share(*args, **kwargs)

Bases: , Model

Share are the entry point to access an Object.

Share provides a set of capabilities for specific receiver. The concrete sub-model MUST provide the target foreign key to an Object.

There are two kind of Share:

  • root: the root Share from which all other Shares to object are derived. Created from the create() class method.

  • derived: Share derived from root or another derived. Created from the method.

This class enforce fields validation at save() and bulk_create().

Concrete Share and Capability

This model is implemented as an abstract in order to have a Share specific to each model (see Object abstract model).

The related subclass is created at the same time as the concrete implementing Share model. The same mechanism also applies on Object for Share, as they both use base metaclass NestedBase. They thus can be customized if required as this example shows:

from caps.models import Share, Capability

class MyShare(Share):
    class Capability(Capability):
        Share = models.ForeignKey(MyShare, models.CASCADE, null=True, related_name="capabilities")
        # custom code here...

    target = models.ForeignKey(MyObject, models.CASCADE)
Cap: int | tuple[int, int]

Capability information, as tuple of (permission_id, max_derive) or single permission id (then max_derive is None).

Capability

alias of ShareCapability

Caps: Iterable[CapabilitySet.Cap]

Many capability information.

class Meta

Bases: object

abstract = False
unique_together = (('origin', 'receiver', 'target'),)
async aderive(items=None, raises=False, defaults={}, **kwargs)

Async version of .

Parameters:
  • items (CapabilitySet.Caps)

  • raises (bool)

  • defaults (dict[str, Any])

Return type:

classmethod create_root(emitter, target, **kwargs)

Create and save a new root Share with provided capabilities.

Return type:

Parameters:
  • emitter ()

  • target (object)

depth

Share chain’s current depth.

derive(receiver, items=None, raises=False, defaults={}, **kwargs)

Create a new Share derived from self.

Parameters:
  • items – select capabilities to be derived.

  • raises – raises PermissionDenied error instead of silent it.

  • defaults – Capability instances’ initial arguments.

  • **kwargs

    initial arguments of the new set.

  • receiver ( | int)

Return type:

property emitter

Agent emitting the Share.

expiration

A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

get_capabilities()
Return type:

is_derived(other)

Return True if capabilities iterable is a subset of self.

Set is a subset of another one if and only if: - all capabilities of subset are in set and derived from set (cf. Capability.is_subset) - there is no capability inside subset that are not in set.

Return type:

bool

Parameters:

other ()

is_valid(raises=False)

Check Share values validity, throwing exception on invalid values.

:returns True if valid, otherwise raise ValueError :yield ValueError: when Share is invaldi

Return type:

bool

Parameters:

raises (bool)

objects
origin

Source Share in Shares chain.

origin_id
receiver

Agent receiving capability.

receiver_id
save(*a, **kw)

Save the current instance. Override this in a subclass if you want to control the saving process.

The ‘force_insert’ and ‘force_update’ parameters can be used to insist that the “save” must be an SQL insert or update (or equivalent for non-SQL backends), respectively. Normally, they should not be set.

uuid

Public Share used in API and with the external world.

class ShareQuerySet(model=None, query=None, using=None, hints=None)

Bases: QuerySet

QuerySet for Share classes.

class Meta

Bases: object

abstract = True
unique_together = (('receiver', 'target', 'emitter'),)
bulk_create(objs, *a, **kw)

Insert each of the instances into the database. Do not call save() on each of the instances, do not send any pre/post_save signals, and do not set the primary key attribute if it is an autoincrement field (except if features.can_return_rows_from_bulk_insert=True). Multi-table models are not supported.

can(permissions)

Filter Shares with the provided permission(s).

This call , using the same parameters. Providing multiple values will make an OR conditional.

If you want to filter based on AND please use .

Parameters:

permissions (CanMany) – permissions to look for

Return type:

can_all(permissions)

Filter Shares with all the provided permissions.

Parameters:

permissions (CanMany) – permissions to look for, same argument type as .

Return type:

emitter(agent)

Shares for the provided emitter(s).

Return type:

Parameters:

agent ( | Iterable[])

receiver(agent)

Shares for the provided receiver(s).

Return type:

Parameters:

agent ( | Iterable[])

access(receiver, uuid)

Share by uuid and receiver(s).

Note that receiver is provided as first parameter in order to enforce its usage. It however can be None: this only should be used when queryset has already been filtered by receiver.

Parameters:
  • receiver ( | Iterable[] | None) – the agent that retrieving the Share.

  • uuid (UUID) – the Share uuid to fetch.

Yield DoesNotExist:

when the Share is not found.

Return type:

accesses(receiver, uuids)

Shares by many uuid and receiver(s).

Please accesser to for more information.

Parameters:
  • receiver ( | Iterable[] | None) – the agent that retrieving the Share.

  • uuids (Iterable[UUID]) – an iterable of uuids to fetch

Return type:

caps.models.object

class Object(*args, **kwargs)

Bases: Model

An object accessible through Shares.

It can have a member Share (subclass of caps.models.Share) that is used as object’s specific Share. If none is provided, a it will be generated automatically for concrete classes.

The Capability concrete model class will be set at creation, when the related is created.

This provides:

  • concrete model accessible from the concrete subclass;

  • concrete model accessible from the concrete subclass;

Capability

alias of ObjectShareCapability

class Meta

Bases: object

abstract = False
Share

alias of ObjectShare

objects
Share

Return Share to this object for receiver provided to ObjectQuerySet’s access() or accesses().

class ObjectBase(name, bases, attrs)

Bases:

Metaclass for Object model classes.

It subclass Share if no Share member is provided.

classmethod create_nested_class(new_class, name, attrs={})

Provide target ForeignKey on nested Share model.

nested_class

alias of

nested_name: str | None = 'Share'

Attribute and class name of the nested class. If not provided, takes it from

class ObjectQuerySet(model=None, query=None, using=None, hints=None)

Bases: QuerySet

QuerySet for Objects.

receiver(receiver)

Filter object for provided.

Return type:

Parameters:

receiver ( | Iterable[])

access(receiver, uuid, accesses=None)

Return Share for provided receiver and uuid.

This method annotates the Object with agent_Share_set whose value is set to relevant Share(s). This allows to use Share property.

Please accesser to ShareQuerySet.access() for more information.

Parameters:
  • receiver ( | Iterable[] | None) – the Share’s receiver

  • uuid (UUID) – Share uuid

  • accesses ( | None) – use this Share QuerySet

Return type:

accesses(receiver, uuids, accesses=None)

Return Shares for provided receiver and uuids.

Please accesser to and ShareQuerySet.accesses() for more information.

Parameters:
  • receiver ( | Iterable[] | None) – the Share’s receiver

  • uuids (Iterable[UUID]) – Share uuids

  • accesses ( | None) – use this Share QuerySet

Return type:

with_accesses(accesses_queryset)

Paccessetch provided Shares. Add Shares paccessetch for objects.

Return type:

Parameters:

accesses_queryset (QuerySet)

caps.models.nested

class NestedBase(name, bases, attrs)

Bases: ModelBase

This metaclass allows to create nested model class based from parent one.

By default, the method will first look for an existing declaration. If none is found it will create one with only the declared as base. This means that you must manually declare any other parent class:

class Nested:
    pass

class ParentBase(NestedBase):
    nested_class = Nested

    @classmethod
    def create_nested_class(cls, name, attrs={}):
        return super(Parent, cls).create_nested_class(
            name,
            {"custom_field": ForeignKey()}
        )

class Parent(metaclass=NestedBase):
    pass

class A(Parent):
    # you must declare its bases
    class Nested(Nested):
        # customize code here
        pass

class C(A, B):
    # you need to be explicit
    class Nested(A.Nested):
        pass
classmethod create_nested_class(new_class, name, attrs={})

Create the nested class for the provided container one to-be-created.

Parameters:
  • new_class (type[object]) – the parent class that just has been created

  • name (str) – nested class name

  • attrs (dict[str, Any]) – nested class attributes

Return type:

type

:return the newly created class.

classmethod get_nested_class(new_class)

Get nested model class or creates a new one.

Parameters:

new_class (type[object])

nested_class: type[object] = None

Nested class base to create.

nested_name: str | None = None

Attribute and class name of the nested class. If not provided, takes it from

new_name: str = '{class_name}{nested_name}'

Name format for newly created classes.

classmethod set_meta(attrs, set={}, defaults={})

Get or create new meta class assigning to it the provided attributes.

Parameters:
  • attrs (dict[str, Any]) – attribute to look into

  • set (dict[str, Any]) – attributes to set to the class

  • defaults (dict[str, Any]) – attributes to set to the class if not present

Return type:

type

:return the Meta class.