Rules

Rule library that forms the core of Bridgekeeper.

This module defines the Rule base class, as well as a number of built-in rules.

The Rule API

class bridgekeeper.rules.Rule[source]

Base class for rules.

All rules are instances of this class, but not directly; use (or write!) a subclass instead, as this class will raise NotImplementedError if you try to actually do anything with it.

check(user, instance=None)[source]

Check if a user satisfies this rule.

Given a user, return a boolean indicating if that user satisfies this rule for a given instance, or if none is provided, every instance.

filter(user, queryset)[source]

Filter a queryset to instances that satisfy this rule.

Given a queryset and a user, this method will return a filtered queryset that contains only instances from the original queryset for which the user satisfies this rule.

Parameters
Returns

A filtered queryset

Return type

django.db.models.QuerySet

Warning

If you are subclassing this class, don’t override this method; override query() instead.

is_possible_for(user)[source]

Check if it is possible for a user to satisfy this rule.

Returns True if it is possible for an instance to exist for which the given user satisfies this rule, False otherwise.

For example, in a multi-tenanted app, you might have a rule that allows access to model instances if a user is a staff user, or if the instance’s tenant matches the user’s tenant.

In that case, check(), when called without an instance, would return True only for staff users (since only they can see every instance). This method would return True for all users, because every user could possibly see an instance (whether it’s one that exists currently in the database, or a hypothetical one that might in the future).

Cases where this method would return False include where a user doesn’t have the right role or subscription plan to use a feature at all; this method is the single-permission equivalent of has-module-perms.

Built-in Blanket Rules

bridgekeeper.rules.always_allow[source]

Rule that always allows access to everything.

bridgekeeper.rules.always_deny[source]

Rule that never allows access to anything.

bridgekeeper.rules.is_authenticated[source]

Rule that allows access to users for whom is_authenticated is True.

bridgekeeper.rules.is_superuser[source]

Rule that allows access to users for whom is_superuser is True.

bridgekeeper.rules.is_staff[source]

Rule that allows access to users for whom is_staff is True.

bridgekeeper.rules.is_active[source]

Rule that allows access to users for whom is_active is True.

Rule Classes

class bridgekeeper.rules.R(**kwargs)[source]

Rules that allow access to some objects but not others.

R takes a set of field lookups as keyword arguments.

Each argument is a model attribute. Foreign keys can be traversed using double underscores, as in Q objects.

The value assigned to each argument can be:

  • A value to match.

  • A function that accepts a user, and returns a value to match.

  • If the argument refers to a foreign key or many-to-many relationship, another Rule object.

class bridgekeeper.rules.Is(instance)[source]

Rule class that checks the identity of the instance.

This rule is satisfied only by a the provided model instance.

Parameters

instance – The instance to match against, or a callable that takes a user and returns a value to match against.

For instance, if you only wanted a user to be able to update their own profile:

own_profile = Is(lambda user: user.profile)
class bridgekeeper.rules.In(collection)[source]

Rule class that checks the instance is a member of a collection.

This rule is satisfied only by model instances that are members of the provided collection.

Parameters

collection – The collection to match against, or a callable that takes a user and returns a value to match against.

For instance, if you only wanted to match groups a user is in:

own_profile = Is(lambda user: user.profile)

Deprecated rule classes

class bridgekeeper.rules.Attribute(attr, matches)[source]

Rule class that checks the value of an instance attribute.

Deprecated since version 0.9: Use R objects instead.

# old
Attribute('colour', matches='blue')
Attribute('tenant', lambda user: user.tenant)

# new
R(colour='blue')
R(tenant=lambda user: user.tenant)

This rule is satisfied by model instances where the attribute given in attr matches the value given in matches.

Parameters
  • attr (str) – An attribute name to match against on the model instance.

  • value – The value to match against, or a callable that takes a user and returns a value to match against.

For instance, if you had a model class Widget with an attribute colour that was either 'red', 'green' or 'blue', you could limit access to blue widgets with the following:

blue_widgets_only = Attribute('colour', matches='blue')

Restricting access in a multi-tenanted application by matching a model’s tenant to the user’s might look like this:

applications_by_tenant = Attribute('tenant',
                                   lambda user: user.tenant)

Warning

This rule uses Python equality (==) when checking a retrieved Python object, but performs an equality check on the database when filtering a QuerySet. Avoid using it with imprecise types (e.g. floats), and ensure that you are using the correct Python type (e.g. decimal.Decimal for decimals rather than floats or strings), to prevent inconsistencies.

class bridgekeeper.rules.Relation(attr, rule)[source]

Check that a rule applies to a ForeignKey.

Deprecated since version 0.9: Use R objects instead.

# old
Relation('applicant', perms['foo.view_applicant'])

# new
R(applicant=perms['foo.view_applicant'])
Parameters
  • attr (str) – Name of a foreign key attribute to check.

  • rule (Rule) – Rule to check the foreign key against.

For example, given Applicant and Application models, to allow access to all applications to anyone who has permission to access the related applicant:

perms['foo.view_application'] = Relation(
    'applicant', perms['foo.view_applicant'])
class bridgekeeper.rules.ManyRelation(query_attr, rule)[source]

Check that a rule applies to a many-object relationship.

Deprecated since version 0.9: Use R objects instead.

# old
ManyRelation('agency', Is(lambda user: user.agency))

# new
R(agency=lambda user: user.agency)

This can be used in a similar fashion to Relation, but across a ManyToManyField, or the remote end of a ForeignKey.

Parameters
  • query_attr (str) – Name of a many-object relationship to check. This This is the name that you use when filtering this relationship using .filter(). If you are on the side of the relationship where the field is defined, this is typically the lowercased model name (e.g. mymodel on its own, not mymodel_set), unless you’ve set related_name or related_query_name.

  • rule (Rule) – Rule to check the foreign object against.

For example, given Agency and Customer models, to allow agency users access only to customers that have a relationship with their agency:

perms['foo.view_customer'] = ManyRelation(
    'agency', Is(lambda user: user.agency))

Built-in rule instances

bridgekeeper.rules.current_user = Is(<function <lambda>>)

This rule is satisfied by the user object itself.

bridgekeeper.rules.in_current_groups = In(<function <lambda>>)

This rule is satisfied by any group the user is in.

Extension Points (For Writing Your Own Rule Subclasses)

class bridgekeeper.rules.Rule[source]

If you want to create your own rule class, these are the methods you need to override.

query(user)[source]

Generate a Q object.

Note

This method is used internally by filter(); subclasses will need to override it but you should never need to call it directly.

Given a user, return a Q object which will filter a queryset down to only instances for which the given user satisfies this rule.

Alternatively, return UNIVERSAL if this user satisfies this rule for every possible object, or EMPTY if this user cannot satisfy this rule for any possible object. (These two values are usually only returned in “blanket rules” which depend only on some property of the user, e.g. the built-in is_staff, but these are usually best created with the blanket_rule decorator.)

Parameters

user (django.contrib.auth.models.User) – The user to match against.

Returns

A query that will filter a queryset to match this rule.

Return type

django.db.models.Q

check(user, instance=None)[source]

Check if a user satisfies this rule.

Given a user, return a boolean indicating if that user satisfies this rule for a given instance, or if none is provided, every instance.

bridgekeeper.rules.UNIVERSAL = UNIVERSAL

A sentinel value that represents the universal set. See Rule.query() for usage.

bridgekeeper.rules.EMPTY = EMPTY

A sentinel value that represents the empty set. See Rule.query() for usage.