Guards

Sometimes, we need more complex permission matching than just simple matching between two sets of scopes. We might for instance need to match on something like: “Does the user have x OR (y and z) BUT NOT w”.

For this purpose, we have the class ScopedPermissionGuard.

Most decorators and permission-attributes in this library can take ScopedPermissionGuard(s) as arguments. And indeed, most of them use this class under-the-hood.

Basic usage

ScopedPermissionGuards in its most simple usage take one or two arguments:

from django_scoped_permissions.guards import ScopedPermissionGuard

ScopedPermissionGuard("scope1:scope2")
ScopedPermissionGuard("scope1", "verb")
# Can also be supplied by kwargs
ScopedPermissionGuard(scope="scope1", verb="read")

The guards can verify that a set of granting permissions has access via the has_permission method:

from django_scoped_permissions.guards import ScopedPermissionGuard

guard = ScopedPermissionGuard(scope="scope1", verb="read")

assert guard.has_permission("scope1")
assert guard.has_permission("scope1:read")
assert guard.has_permission(["read", "scope3"])
assert not guard.has_permission("scope2")

Combining guards and operators

The power of guards, however, is unleashed when we combine guards with boolean operators. E.g:

from django_scoped_permissions.guards import ScopedPermissionGuard

guard_1 = ScopedPermissionGuard(scope="scope1", verb="read")
guard_2 = ScopedPermissionGuard("scope2")

# This guard requires you to have scope1:read AND scope2
guard_3 = guard_1 & guard_2

# This guard requires you to have scope1:read OR NOT scope2
guard_4 = guard_1 | ~guard_2

# This guard requires you to have scope1:read and scope2 XOR (not scope1 and scope3)
guard_5 = (guard_1 & guard_2) ^ (ScopedPermissionGuard("scope1") & ScopedPermissionGuard("scope3"))

assert guard_4.has_permission(["scope1", "scope2"])
assert guard_4.has_permission(["scope3"])
assert not guard_4.has_permission(["scope3", "scope2"])

assert guard_5.has_permission(["scope1:read", "scope2"])
assert guard_5.has_permission(["scope3"])

Supported operators are:

  • &: AND

  • |: OR

  • ^: XOR

  • ~: NOT

Usage in practice

ScopedPermissionGuards can be used in all decorators and properties where permissions are supplied:

@gql_has_permission(
    ScopedPermissionGuard(
        scope="user", verb="read") &
    ScopedPermissionGuard("organization:{context.organization.id}:read")
)
def resolve_company_user(self, info, **kwargs):
    pass