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