✨(back) check on document update if user can save it
When a document is updated, users not connected to the collaboration server can override work made by other people connected to the collaboration server. To avoid this, the priority is given to user connected to the collaboration server. If the websocket property in the request payload is missing or set to False, the backend fetch the collaboration server to now if the user can save or not. If users are already connected, the user can't save. Also, only one user without websocket can save a connect, the first user saving acquire a lock and all other users can't save. To implement this behavior, we need to track all users, connected and not, so a session is created for every user in the ForceSessionMiddleware.
This commit is contained in:
@@ -32,6 +32,7 @@ from rest_framework import filters, status, viewsets
|
||||
from rest_framework import response as drf_response
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.throttling import UserRateThrottle
|
||||
from sentry_sdk import capture_exception
|
||||
|
||||
from core import authentication, enums, models
|
||||
from core.services.ai_services import AIService
|
||||
@@ -634,6 +635,54 @@ class DocumentViewSet(
|
||||
"""Override to implement a soft delete instead of dumping the record in database."""
|
||||
instance.soft_delete()
|
||||
|
||||
def perform_update(self, serializer):
|
||||
"""Check rules about collaboration."""
|
||||
if serializer.validated_data.get("websocket"):
|
||||
return super().perform_update(serializer)
|
||||
|
||||
try:
|
||||
connection_info = CollaborationService().get_document_connection_info(
|
||||
serializer.instance.id,
|
||||
self.request.session.session_key,
|
||||
)
|
||||
except requests.HTTPError as e:
|
||||
capture_exception(e)
|
||||
connection_info = {
|
||||
"count": 0,
|
||||
"exists": False,
|
||||
}
|
||||
|
||||
if connection_info["count"] == 0:
|
||||
# No websocket mode
|
||||
logger.debug("update without connection found in the websocket server")
|
||||
cache_key = f"docs:no-websocket:{serializer.instance.id}"
|
||||
current_editor = cache.get(cache_key)
|
||||
if not current_editor:
|
||||
cache.set(
|
||||
cache_key,
|
||||
self.request.session.session_key,
|
||||
settings.NO_WEBSOCKET_CACHE_TIMEOUT,
|
||||
)
|
||||
elif current_editor != self.request.session.session_key:
|
||||
raise drf.exceptions.PermissionDenied(
|
||||
"You are not allowed to edit this document."
|
||||
)
|
||||
cache.touch(cache_key, settings.NO_WEBSOCKET_CACHE_TIMEOUT)
|
||||
return super().perform_update(serializer)
|
||||
|
||||
if connection_info["exists"]:
|
||||
# Websocket mode
|
||||
logger.debug("session key found in the websocket server")
|
||||
return super().perform_update(serializer)
|
||||
|
||||
logger.debug(
|
||||
"Users connected to the websocket but current editor not connected to it. Can not edit."
|
||||
)
|
||||
|
||||
raise drf.exceptions.PermissionDenied(
|
||||
"You are not allowed to edit this document."
|
||||
)
|
||||
|
||||
@drf.decorators.action(
|
||||
detail=False,
|
||||
methods=["get"],
|
||||
|
||||
Reference in New Issue
Block a user