Static Role Permissions
This library provides a highly simplified permission system based on a static (hardcoded) definition of roles and permissions.
This saves performance, because it does not require any database queries to check permissions.
This saves complexity, because the permissions are defined in a simple dictionary. No n:m relations, no changes at runtime.
Installation
Add
StaticRolePermissionBackendto yourAUTHENTICATION_BACKENDSin thesettings.py:
AUTHENTICATION_BACKENDS = [
# ... other backends
# 'django.contrib.auth.backends.ModelBackend', <-- replace the default ModelBackend
"ambient_toolbox.static_role_permissions.auth_backend.StaticRolePermissionBackend",
]
Set
STATIC_ROLE_PERMISSIONS_PATHas dotted path, pointing to a dictionary, which defines your permissions per role.
Example:
settings.py
STATIC_ROLE_PERMISSIONS_PATH = "my_project.conf.permisions.PERMISSIONS_DICT"
my_project.conf.permisions.py
PERMISSIONS_DICT: dict[str, set[str]] = {
"role_1": {"my_app.permission_1", "my_app.permission_2", ...},
"role_2": {...},
}
The roles (keys) and permissions (values) are simple strings.
By default, a system check will match the values against the Django model permission (e.g. polls.view_poll)
(see System check for more information).
In theory, roles and permissions could be any hashable value. Just make sure that the return value of User.role
matches the keys of the dictionary.
Add
ambient_toolboxto yourINSTALLED_APPSin thesettings.pyif not already done:
INSTALLED_APPS = [
# ... other apps
"ambient_toolbox",
]
This is needed to register the system check.
Simple example
PERMISSIONS_DICT: dict[str, set[str]] = {
"admin": {
"auth.view_user",
"auth.edit_user",
},
"customer": {
"auth.view_user",
},
}
class User(AbstractUser):
@property
def role(self) -> str:
return "admin" if self.is_staff else "customer"
User(is_staff=True).has_perm("auth.edit_user") # --> True (without any db query)
Example with role field
This library does not require a role field on the user model, but here is an example how you could implement it:
class UserRoleChoices(models.TextChoices):
ADMIN = "ADMIN", "Admin"
EMPLOYEE = "EMPLOYEE", "Employee"
USER = "USER", "User"
class User(AbstractUser):
role = models.CharField(
max_length=100,
choices=UserRoleChoices.choices,
default=UserRoleChoices.USER,
)
PERMISSIONS_DICT: dict[str, set[str]] = {
UserRoleChoices.ADMIN: {...},
UserRoleChoices.EMPLOYEE: {...},
UserRoleChoices.USER: {...},
}
System check
By default, this library will register a system check if STATIC_ROLE_PERMISSIONS_PATH is set.
The check will go through all the defined permissions and compare them with the Django model permissions.
If any permission does not exist in the Django permission system, a warning will be raised.
This is useful to ensure compatibility with the Django ecosystem and to avoid typos in the permissions.
The system check can be disabled by setting STATIC_ROLE_PERMISSIONS_ENABLE_SYSTEM_CHECK to False in the settings.
Why use this library?
Django comes by default with a quite sophisticated permission system. It is very flexible and covers a lot of use cases for user-based permissions.
Django permission system in a nutshell:
User have permissions (n:m)
Users have groups (n:m)
Groups have permissions (n:m)
So a user has all permissions of their groups and their own permissions combined
Permissions are strings like
app_label.permission_name(e.g.polls.view_poll)Groups, Permissions and their relations are stored in the db and can be managed via the admin interface at runtime
However, in many cases, this adds a lot of complexities to the application, which is not needed.
When users should only have one group at a time, you still need to manage an n:m relation
When permissions should not change at runtime, you still need to create and query the permissions via your database.
When your authorisation logic is not based on permissions at all, you probably still need to deal with permission, e.g. in Django Admin, because the Django ecosystem is built around this system.
This library provides a much simpler way to manage permissions, which is based roles and a hardcoded set of permissions per role.
Limitations
This library comes with a simple approach for roles / permissions. If your reality is more complex than that, you should not use it.
E.g when:
Users should have more than one role at a time
Permissions should be managed at runtime
You need to assign permissions to individual users