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
queryset (django.db.models.QuerySet) – The initial queryset to filter
user (django.contrib.auth.models.User) – The user to match against.
- 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 returnTrue
only for staff users (since only they can see every instance). This method would returnTrue
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.
is_authenticated
[source]¶ Rule that allows access to users for whom
is_authenticated
isTrue
.
-
bridgekeeper.rules.
is_superuser
[source]¶ Rule that allows access to users for whom
is_superuser
isTrue
.
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 inmatches
.- 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 attributecolour
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
For example, given
Applicant
andApplication
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 aManyToManyField
, or the remote end of aForeignKey
.- 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, notmymodel_set
), unless you’ve setrelated_name
orrelated_query_name
.rule (Rule) – Rule to check the foreign object against.
For example, given
Agency
andCustomer
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, orEMPTY
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-inis_staff
, but these are usually best created with theblanket_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
-
-
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.