From 394725754014586ee71b8b4c7365a1a318d0c96d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20K=C3=BCnsem=C3=B6ller?= <jkuensem@physik.uni-bielefeld.de> Date: Tue, 22 Aug 2023 11:16:59 +0200 Subject: [PATCH] TMSS-2658: enable permission checks for websockets again, with logging, but for now keep sending to everyone. --- .../websocket/lib/websocket_service.py | 129 ++++++++---------- 1 file changed, 60 insertions(+), 69 deletions(-) diff --git a/SAS/TMSS/backend/services/websocket/lib/websocket_service.py b/SAS/TMSS/backend/services/websocket/lib/websocket_service.py index 74e3a971000..72e07fc9bdd 100644 --- a/SAS/TMSS/backend/services/websocket/lib/websocket_service.py +++ b/SAS/TMSS/backend/services/websocket/lib/websocket_service.py @@ -55,33 +55,25 @@ class TMSSWebSocket(WebSocket): self.authenticated = False self.user = None - # JS 2023-08-11: TODO: fix this! For now we want all users to get updates. - self.authenticated = True - def handleMessage(self): - # JS 2023-08-11: TODO: fix this! For now we want all users to get updates. - self._set_flags_to_default() - return - - # try: - # if not self.authenticated: # Not (yet) authenticated - # token_key = JSONloads(self.data).get('token', '') - # - # from rest_framework.authtoken.models import Token - # token_obj = Token.objects.filter(key=token_key).first() - # if token_obj: - # self.user = token_obj.user - # self.authenticated = True - # logger.info('Client authenticated. User: %s from IP: %s' % (self.user, self.address[0])) - # else: - # logger.info('Client not authenticated. IP: %s' % (self.address[0])) - # self.close(1011, u'Please login, so you have a token, and please submit the token in the 1st message after the connection was made.') - # else: - # logger.debug('Client already authenticated, ignoring incoming message. User: %s from IP: %s' % (self.user, self.address[0])) - # # NOTE: We just ignore incoming messages as we treat the communication as one-way only, except for the auth msg. - # except Exception as e: - # logger.exception('Error when handling websocket message of User: %s from IP: %s' % (self.user, self.address[0])) - # raise + try: + if not self.authenticated: # Not (yet) authenticated + token_key = JSONloads(self.data).get('token', '') + from rest_framework.authtoken.models import Token + token_obj = Token.objects.filter(key=token_key).first() + if token_obj: + self.user = token_obj.user + self.authenticated = True + logger.info('Client authenticated. User: %s from IP: %s' % (self.user, self.address[0])) + else: + logger.info('Client not authenticated. IP: %s' % (self.address[0])) + self.close(1011, u'Please login, so you have a token, and please submit the token in the 1st message after the connection was made.') + else: + logger.debug('Client already authenticated, ignoring incoming message. User: %s from IP: %s' % (self.user, self.address[0])) + # NOTE: We just ignore incoming messages as we treat the communication as one-way only, except for the auth msg. + except Exception as e: + logger.exception('Error when handling websocket message of User: %s from IP: %s' % (self.user, self.address[0])) + raise def handleConnected(self): # Enforce to initial values be safe @@ -138,49 +130,48 @@ class TMSSEventMessageHandlerForWebsocket(TMSSEventMessageHandler): self.t.join() def _get_authorised_clients_for_object_in_websocket(self, obj): - # JS 2023-08-11: TODO: fix this! For now we want all users to get updates. - return list(self._ws_server.connections.values()) - - # from django.contrib.auth import get_user_model - # User = get_user_model() - # - # from lofar.sas.tmss.tmss.tmssapp.viewsets.permissions import get_project_roles_for_user, get_project_roles_with_permission - # from lofar.sas.tmss.tmss.tmssapp.models import ProjectRole - # - # auth_clients = [] - # logger.debug('Checking which of these users should receive websocket update for obj=%s: %s' % (obj, [ws.user for ws in list(self._ws_server.connections.values())])) - # for ws in list(self._ws_server.connections.values()): - # if ws.authenticated: # Check user permissions for the object - # # JS 2023-08-11: TODO: fix this! For now we want all users to get updates. - # auth_clients.append(ws) - # continue - # - # user = User.objects.filter(username=ws.user).first() - # if user is None: - # continue - # - # if user.is_superuser: - # logger.debug('User=%s is superuser and will receive websocket update for obj=%s' % (user, obj)) - # auth_clients.append(ws) - # elif user.has_perm("tmssapp.view_%s" % type(obj).__name__.lower()): - # logger.debug('User=%s has permission=%s and will receive websocket update for obj=%s' % (user, "tmssapp.view_%s" % type(obj).__name__.lower(), obj)) - # auth_clients.append(ws) - # else: - # logger.debug('User=%s has no permission=%s, checking for project-based permission to receive websocket update for obj=%s' % (user, "tmssapp.view_%s" % type(obj).__name__.lower(), obj)) - # # project-based permission - # permitted_project_roles = get_project_roles_with_permission(type(obj).__name__.lower(), 'GET') - # user_project_roles = get_project_roles_for_user(user) - # related_project = getattr(obj, 'project', None) - # for project_role in user_project_roles: - # if related_project: - # if project_role['project'].lower() == related_project.name.lower() and \ - # ProjectRole.objects.get(value=project_role['role']) in permitted_project_roles: - # auth_clients.append(ws) - # logger.debug("User=%s has project-based permission for project=%s and will receive websocket update for obj=%s" % (user, project_role['project'].lower(), obj)) - # break - # else: - # logger.debug("%s websocket is not authenticated and will not receive websocket update for obj=%s" % (ws.user, obj)) - # return auth_clients + from django.contrib.auth import get_user_model + User = get_user_model() + + from lofar.sas.tmss.tmss.tmssapp.viewsets.permissions import get_project_roles_for_user, get_project_roles_with_permission + from lofar.sas.tmss.tmss.tmssapp.models import ProjectRole + + auth_clients = [] + logger.info('Checking which of these users should receive websocket update for obj=%s: %s' % (obj, [ws.user for ws in list(self._ws_server.connections.values())])) + for ws in list(self._ws_server.connections.values()): + if ws.authenticated: # Check user permissions for the object + user = User.objects.filter(username=ws.user).first() + if user is None: + logger.info('User=%s does not exist in TMSS and will not receive websocket update for obj=%s' % (ws.user, obj)) + continue + if user.is_superuser: + logger.info('User=%s is superuser and will receive websocket update for obj=%s' % (user, obj)) + auth_clients.append(ws) + elif user.has_perm("tmssapp.view_%s" % type(obj).__name__.lower()): + logger.info('User=%s has permission=%s and will receive websocket update for obj=%s' % (user, "tmssapp.view_%s" % type(obj).__name__.lower(), obj)) + auth_clients.append(ws) + else: + logger.info('User=%s has no permission=%s, checking for project-based permission to receive websocket update for obj=%s' % (user, "tmssapp.view_%s" % type(obj).__name__.lower(), obj)) + # project-based permission + permitted_project_roles = get_project_roles_with_permission(type(obj).__name__.lower(), 'GET') + user_project_roles = get_project_roles_for_user(user) + related_project = getattr(obj, 'project', None) + for project_role in user_project_roles: + if related_project: + if project_role['project'].lower() == related_project.name.lower() and \ + ProjectRole.objects.get(value=project_role['role']) in permitted_project_roles: + auth_clients.append(ws) + logger.info("User=%s has project-based permission for project=%s and will receive websocket update for obj=%s" % (user, project_role['project'].lower(), obj)) + break + else: + logger.info("%s websocket is not authenticated and will not receive websocket update for obj=%s" % (ws.user, obj)) + + # todo: remove this, once the above works as expected. -> TMSS-2658 + if ws not in auth_clients: + logger.warning('Workaround: %s websocket will receive websocket update for obj=%s despite failed permission check' % (ws.user, obj)) + auth_clients.append(ws) + + return auth_clients def _broadcast_notify_to_clients_websocket(self, msg, clients): # Send a broadcast message to all ws clients passed as argument -- GitLab