Custom Permissions¶
The permission system has two layers: async PermissionHandler (the public
interface) and synchronous PermissionHelperMixin (the DB logic). Override
either to customise authorisation.
The Simplest Override¶
Subclass PermissionHandler and override the async method you want to change:
# myapp/permissions.py
from realtime_chat_messaging.permissions.handlers import PermissionHandler
class CustomPermissionHandler(PermissionHandler):
async def have_send_message_permission(self, user, data):
"""
Custom send permission: staff users can send to any room.
Returns: (is_permitted: bool, room: Room)
"""
if user.is_staff:
from realtime_chat_messaging.utils.loader import get_model
from realtime_chat_messaging.utils.decorators import sqlite_safe_db_sync_to_async
from django.shortcuts import get_object_or_404
@sqlite_safe_db_sync_to_async
def get_room():
Room = get_model("Room")
room_id = data.get("room_id")
return True, get_object_or_404(Room, pk=room_id)
return await get_room()
# Fall back to default for non-staff users
return await super().have_send_message_permission(user, data)
Register it:
REALTIME_CHAT_MESSAGING = {
"PERMISSION_HANDLER_CLASS": "myapp.permissions.CustomPermissionHandler",
}
Overriding the Sync Helper Layer¶
For more involved changes, override the synchronous _have_* methods in
PermissionHelperMixin and use sqlite_safe_db_sync_to_async to wrap them:
from realtime_chat_messaging.permissions.handlers import PermissionHandler
from realtime_chat_messaging.utils.decorators import sqlite_safe_db_sync_to_async
from realtime_chat_messaging.utils.loader import get_model
class CustomPermissionHandler(PermissionHandler):
@sqlite_safe_db_sync_to_async
def have_room_permission(self, user, room_id):
"""
Allow access to archived rooms for their members.
"""
Room = get_model("Room")
from django.shortcuts import get_object_or_404
room = get_object_or_404(Room, pk=room_id)
# Custom: check archived rooms table too
is_member = self._is_member(user, room)
return is_member, room
@staticmethod
def _is_member(user, room):
if hasattr(room, "participants"):
return room.participants.filter(pk=user.pk).exists()
elif hasattr(room, "subscribers"):
return room.subscribers.filter(pk=user.pk).exists()
return False
Method Return Types¶
All permission handler methods must return the same types as the defaults:
Method |
Return type |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
The default_admin_names Parameter¶
Several helper methods accept a default_admin_names keyword:
default_admin_names = {"group": "admins", "channel": "moderators"}
This maps the GroupChat and Channel admin M2M field names. If you created a
custom GroupChat model that renamed admins to managers, override the
helper and pass the updated mapping:
class CustomPermissionHandler(PermissionHandler):
@sqlite_safe_db_sync_to_async
def have_room_permissions_to_add_or_remove_members(self, user, room_id, perm_phrase):
return self._have_room_permissions_to_add_or_remove_members(
user,
room_id,
perm_phrase,
default_admin_names={"group": "managers", "channel": "moderators"},
)