implement some auth for MeshUIConsumer

This commit is contained in:
Laura Klünder 2023-11-09 17:04:55 +01:00
parent 88d6f07eaf
commit 394450f4a3
4 changed files with 30 additions and 9 deletions

View file

@ -3,8 +3,10 @@ from contextlib import suppress
from channels.auth import AuthMiddlewareStack from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
from c3nav.control.middleware import UserPermissionsChannelMiddleware
from c3nav.urls import websocket_urlpatterns from c3nav.urls import websocket_urlpatterns
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "c3nav.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "c3nav.settings")
@ -12,8 +14,12 @@ django_asgi = get_asgi_application()
application = ProtocolTypeRouter({ application = ProtocolTypeRouter({
"http": django_asgi, "http": django_asgi,
"websocket": AuthMiddlewareStack( "websocket": AllowedHostsOriginValidator(
URLRouter(websocket_urlpatterns) AuthMiddlewareStack(
UserPermissionsChannelMiddleware(
URLRouter(websocket_urlpatterns),
),
),
), ),
}) })
@ -30,7 +36,4 @@ with suppress(ImportError):
Mount(settings.STATIC_URL, app=StaticFiles(directory=settings.STATIC_ROOT), name='static'), Mount(settings.STATIC_URL, app=StaticFiles(directory=settings.STATIC_ROOT), name='static'),
Mount('/', app=django_asgi), Mount('/', app=django_asgi),
]), ]),
"websocket": AuthMiddlewareStack(
URLRouter(websocket_urlpatterns)
),
}) })

View file

@ -1,8 +1,15 @@
from django.utils.functional import SimpleLazyObject, lazy from channels.db import database_sync_to_async
from channels.middleware import BaseMiddleware as BaseChannelsMiddleware
from django.utils.functional import LazyObject, SimpleLazyObject, lazy
from c3nav.control.models import UserPermissions, UserSpaceAccess from c3nav.control.models import UserPermissions, UserSpaceAccess
class UserPermissionsLazyObject(LazyObject):
def _setup(self):
raise ValueError("Accessing scope user before it is ready.")
class UserPermissionsMiddleware: class UserPermissionsMiddleware:
""" """
This middleware adds request.user_permissions to get the UserPermissions for the current request/user. This middleware adds request.user_permissions to get the UserPermissions for the current request/user.
@ -32,3 +39,12 @@ class UserPermissionsMiddleware:
request.user_permissions = SimpleLazyObject(lambda: self.get_user_permissions(request)) request.user_permissions = SimpleLazyObject(lambda: self.get_user_permissions(request))
request.user_space_accesses = lazy(self.get_user_space_accesses, dict)(request) request.user_space_accesses = lazy(self.get_user_space_accesses, dict)(request)
return self.get_response(request) return self.get_response(request)
class UserPermissionsChannelMiddleware(BaseChannelsMiddleware):
async def __call__(self, scope, receive, send):
# todo: this doesn't seem to actually be lazy. and scope["user"] isn't either?
scope["user_permissions"] = UserPermissionsLazyObject()
scope["user_permissions"]._wrapped = await database_sync_to_async(UserPermissions.get_for_user)(scope["user"])
return await super().__call__(scope, receive, send)

View file

@ -1,5 +1,5 @@
from contextlib import contextmanager from contextlib import contextmanager
from typing import Dict from typing import Dict, Self
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -73,7 +73,7 @@ class UserPermissions(models.Model):
yield yield
@classmethod @classmethod
def get_for_user(cls, user, force=False) -> 'UserPermissions': def get_for_user(cls, user, force=False) -> Self:
if not user.is_authenticated: if not user.is_authenticated:
return cls() return cls()
cache_key = cls.get_cache_key(user.pk) cache_key = cls.get_cache_key(user.pk)

View file

@ -5,6 +5,7 @@ from functools import cached_property
from asgiref.sync import async_to_sync from asgiref.sync import async_to_sync
from channels.db import database_sync_to_async from channels.db import database_sync_to_async
from channels.exceptions import DenyConnection
from channels.generic.websocket import AsyncJsonWebsocketConsumer, AsyncWebsocketConsumer from channels.generic.websocket import AsyncJsonWebsocketConsumer, AsyncWebsocketConsumer
from django.db import transaction from django.db import transaction
from django.utils import timezone from django.utils import timezone
@ -347,7 +348,8 @@ class MeshUIConsumer(AsyncJsonWebsocketConsumer):
self.msg_received_filter = {} self.msg_received_filter = {}
async def connect(self): async def connect(self):
# todo: auth if not self.scope["user_permisions"].mesh_control:
raise DenyConnection
await self.accept() await self.accept()
async def receive_json(self, content, **kwargs): async def receive_json(self, content, **kwargs):