Django 通道自定义权限系统

问题描述

所以我有一个系统,用户可以通过模型成员成为称为框的模型的一部分。

成员模型有自己的一组角色,而这些角色又拥有自己的权限。

我有特定的方法来确定成员在框中拥有哪一组权限。

所以现在我有一个名为“Box_{Box_id}”的 websocket 组,成员可以连接到该组。诸如框相关模型创建之类的出站事件被发送到该组。

但是,某些成员不应根据其拥有的权限收听某些发送的事件。

这是将发送到表示事件的组的示例消息 {'事件':事件类型, '数据':事件数据}

所以现在,例如,如果用户在框中没有 READ_UPLOADS 权限,则他无法侦听类型为 UPLOAD_CREATE 的事件

如何使用 django 频道实现此类检查?

编辑

class LocalEventsConsumer(AsyncWebsocketConsumer):
    """
    An Asgi consumer for Box-specific (local) event sending.
    Any valid member for the given Box can connect to this consumer.
    """
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        self.Box_id = self.scope['url_route']['kwargs']['Box_id']
        self.events_group_name = 'Box_%s_events' % self.Box_id

        self.overwrites_cache = {}
        self.permissions_cache = set()
        # need to update cache on role and overwrite updates

    async def connect(self):
        try:
            # we cache the member object on connection
            # to help check permissions later on during
            # firing of events
            member_kwargs = {
                'user': self.scope['user'],'Box__id': self.Box_id,}
            self.member = api_models.Member.objects.get(**member_kwargs)
            self.permissions_cache = self.member.base_permissions
        except ObjectDoesNotExist:
            # we reject the connection if the
            # Box-id passed in the url was invalid
            # or the user isn't a member of the Box yet
            await self.close()

        await self.channel_layer.group_add(self.events_group_name,self.channel_name)
        await self.accept()

    async def disconnect(self,close_code):
        await self.channel_layer.group_discard(self.events_group_name,self.channel_name)

    async def fire_event(self,event: dict):
        member_permissions = self.get_event_permissions(event)
        required_permissions = event.pop('listener_permissions',set())
        if required_permissions in member_permissions:
            await self.send(event)

    def get_event_permissions(self,event):
        # handle permission caching throughout
        # the life of the user's connection
        overwrite_channel = event['data'].get('channel',None)
        overwrite_cache = self.overwrites_cache.get(overwrite_channel.id,None)
        if not overwrite_channel:
            # calculate overwrites if the event data at hand
            # has a channel attribute. We would need to calculate
            # overwrites only when channel-specific events are
            # triggered,like UPLOAD_CREATE and OVERWRITE_DELETE
            return self.permissions_cache
        if not overwrite_cache:
            overwrite_cache = self.member.permissions.get_overwrites(overwrite_channel)
            self.overwrites_cache[overwrite_channel.id] = overwrite_cache
        return overwrite_cache

    @receiver(post_delete,sender=api_models.MemberRole)
    @receiver(post_save,sender=api_models.MemberRole)
    def update_permissions_cache(self,instance=None,**kwargs):
        if instance.member == self.member:
            self.permissions_cache = self.member.base_permissions

    @receiver(post_delete,sender=api_models.Overwrite)
    @receiver(post_save,sender=api_models.Overwrite)
    def update_overwrites_cache(self,**kwargs):
        overwrite_cache = self.overwrites_cache.get(instance.channel,None)
        if instance.role in self.member.roles.all() and overwrite_cache:
            self.overwrites_cache[instance.channel] = self.member.permissions.get_overwrites(instance.channel)

这是我当前的消费者。我在消费者之外使用 fire_event 类型。但是,每次需要获取权限时,我都需要访问数据库。因此,我实现了这个权限缓存系统来缓解同样的问题。是否应该更改相同的内容

解决方法

您可以在向客户端发送数据的方法中检查这些权限。由于它们都属于同一个频道组,因此您无法在发送到组的级别过滤掉,至少据我所知是这样。所以你可以做这样的事情:

def receive(self,event):
    # update box
    ...
    # notify the members
    self.channel_layer.group_send(
        f'box_{self.box.id}',{'type': 'notify_box_update','event': EVENT TYPE,'data': EVENT DATA},)

def notify_box_update(event):
    if has_permission(self.user,event['event'],self.box):
        self.send(event)

这里,通知事件通过 channel_layer 发送到组,但只有具有适当权限的用户才能将其发送到下游。您可以在代码中的某处实现 has_permission 方法,以检查给定用户、框和事件类型的权限。