🔥(back) remove collaboration-auth endpoint
We don't need anymore the collaboration-auth endpoint. Every code related to it is removed.
This commit is contained in:
@@ -15,6 +15,7 @@ and this project adheres to
|
|||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
- ♻️(frontend) Integrate UI kit #783
|
- ♻️(frontend) Integrate UI kit #783
|
||||||
|
- 🏗️(y-provider) manage auth in y-provider app
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -380,10 +380,7 @@ class DocumentViewSet(
|
|||||||
9. **Media Auth**: Authorize access to document media.
|
9. **Media Auth**: Authorize access to document media.
|
||||||
Example: GET /documents/media-auth/
|
Example: GET /documents/media-auth/
|
||||||
|
|
||||||
10. **Collaboration Auth**: Authorize access to the collaboration server for a document.
|
10. **AI Transform**: Apply a transformation action on a piece of text with AI.
|
||||||
Example: GET /documents/collaboration-auth/
|
|
||||||
|
|
||||||
11. **AI Transform**: Apply a transformation action on a piece of text with AI.
|
|
||||||
Example: POST /documents/{id}/ai-transform/
|
Example: POST /documents/{id}/ai-transform/
|
||||||
Expected data:
|
Expected data:
|
||||||
- text (str): The input text.
|
- text (str): The input text.
|
||||||
@@ -391,7 +388,7 @@ class DocumentViewSet(
|
|||||||
Returns: JSON response with the processed text.
|
Returns: JSON response with the processed text.
|
||||||
Throttled by: AIDocumentRateThrottle, AIUserRateThrottle.
|
Throttled by: AIDocumentRateThrottle, AIUserRateThrottle.
|
||||||
|
|
||||||
12. **AI Translate**: Translate a piece of text with AI.
|
11. **AI Translate**: Translate a piece of text with AI.
|
||||||
Example: POST /documents/{id}/ai-translate/
|
Example: POST /documents/{id}/ai-translate/
|
||||||
Expected data:
|
Expected data:
|
||||||
- text (str): The input text.
|
- text (str): The input text.
|
||||||
@@ -1207,17 +1204,6 @@ class DocumentViewSet(
|
|||||||
logger.debug("Failed to extract parameters from subrequest URL: %s", exc)
|
logger.debug("Failed to extract parameters from subrequest URL: %s", exc)
|
||||||
raise drf.exceptions.PermissionDenied() from exc
|
raise drf.exceptions.PermissionDenied() from exc
|
||||||
|
|
||||||
def _auth_get_document(self, pk):
|
|
||||||
"""
|
|
||||||
Retrieves the document corresponding to the given primary key (pk).
|
|
||||||
Raises PermissionDenied if the document is not found.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return models.Document.objects.get(pk=pk)
|
|
||||||
except models.Document.DoesNotExist as exc:
|
|
||||||
logger.debug("Document with ID '%s' does not exist", pk)
|
|
||||||
raise drf.exceptions.PermissionDenied() from exc
|
|
||||||
|
|
||||||
@drf.decorators.action(detail=False, methods=["get"], url_path="media-auth")
|
@drf.decorators.action(detail=False, methods=["get"], url_path="media-auth")
|
||||||
def media_auth(self, request, *args, **kwargs):
|
def media_auth(self, request, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
@@ -1265,42 +1251,6 @@ class DocumentViewSet(
|
|||||||
|
|
||||||
return drf.response.Response("authorized", headers=request.headers, status=200)
|
return drf.response.Response("authorized", headers=request.headers, status=200)
|
||||||
|
|
||||||
@drf.decorators.action(detail=False, methods=["get"], url_path="collaboration-auth")
|
|
||||||
def collaboration_auth(self, request, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
This view is used by an Nginx subrequest to control access to a document's
|
|
||||||
collaboration server.
|
|
||||||
"""
|
|
||||||
parsed_url = self._auth_get_original_url(request)
|
|
||||||
url_params = self._auth_get_url_params(
|
|
||||||
enums.COLLABORATION_WS_URL_PATTERN, parsed_url.query
|
|
||||||
)
|
|
||||||
document = self._auth_get_document(url_params["pk"])
|
|
||||||
|
|
||||||
abilities = document.get_abilities(request.user)
|
|
||||||
if not abilities.get(self.action, False):
|
|
||||||
logger.debug(
|
|
||||||
"User '%s' lacks permission for document '%s'",
|
|
||||||
request.user,
|
|
||||||
document.pk,
|
|
||||||
)
|
|
||||||
raise drf.exceptions.PermissionDenied()
|
|
||||||
|
|
||||||
if not settings.COLLABORATION_SERVER_SECRET:
|
|
||||||
logger.debug("Collaboration server secret is not defined")
|
|
||||||
raise drf.exceptions.PermissionDenied()
|
|
||||||
|
|
||||||
# Add the collaboration server secret token to the headers
|
|
||||||
headers = {
|
|
||||||
"Authorization": settings.COLLABORATION_SERVER_SECRET,
|
|
||||||
"X-Can-Edit": str(abilities["partial_update"]),
|
|
||||||
}
|
|
||||||
|
|
||||||
if request.user.is_authenticated:
|
|
||||||
headers["X-User-Id"] = str(request.user.id)
|
|
||||||
|
|
||||||
return drf.response.Response("authorized", headers=headers, status=200)
|
|
||||||
|
|
||||||
@drf.decorators.action(
|
@drf.decorators.action(
|
||||||
detail=True,
|
detail=True,
|
||||||
methods=["post"],
|
methods=["post"],
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ MEDIA_STORAGE_URL_PATTERN = re.compile(
|
|||||||
MEDIA_STORAGE_URL_EXTRACT = re.compile(
|
MEDIA_STORAGE_URL_EXTRACT = re.compile(
|
||||||
f"{settings.MEDIA_URL:s}({UUID_REGEX}/{ATTACHMENTS_FOLDER}/{UUID_REGEX}{FILE_EXT_REGEX})"
|
f"{settings.MEDIA_URL:s}({UUID_REGEX}/{ATTACHMENTS_FOLDER}/{UUID_REGEX}{FILE_EXT_REGEX})"
|
||||||
)
|
)
|
||||||
COLLABORATION_WS_URL_PATTERN = re.compile(rf"(?:^|&)room=(?P<pk>{UUID_REGEX})(?:&|$)")
|
|
||||||
|
|
||||||
|
|
||||||
# In Django's code base, `LANGUAGES` is set by default with all supported languages.
|
# In Django's code base, `LANGUAGES` is set by default with all supported languages.
|
||||||
|
|||||||
@@ -1,185 +0,0 @@
|
|||||||
"""
|
|
||||||
Test collaboration websocket access API endpoint for users in impress's core app.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from uuid import uuid4
|
|
||||||
|
|
||||||
from django.test import override_settings
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from rest_framework.test import APIClient
|
|
||||||
|
|
||||||
from core import factories, models
|
|
||||||
from core.tests.conftest import TEAM, USER, VIA
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
|
||||||
|
|
||||||
|
|
||||||
def test_api_documents_collaboration_auth_unkown_document():
|
|
||||||
"""
|
|
||||||
Trying to connect to the collaboration server on a document ID that does not exist
|
|
||||||
should not have the side effect to create it (no regression test).
|
|
||||||
"""
|
|
||||||
original_url = f"http://localhost/collaboration/ws/?room={uuid4()!s}"
|
|
||||||
|
|
||||||
response = APIClient().get(
|
|
||||||
"/api/v1.0/documents/collaboration-auth/", HTTP_X_ORIGINAL_URL=original_url
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 403
|
|
||||||
assert models.Document.objects.exists() is False
|
|
||||||
|
|
||||||
|
|
||||||
def test_api_documents_collaboration_auth_original_url_not_matching():
|
|
||||||
"""
|
|
||||||
Trying to authenticate on the collaboration server with an invalid
|
|
||||||
original url should return a 403.
|
|
||||||
"""
|
|
||||||
document = factories.DocumentFactory(link_reach="public")
|
|
||||||
|
|
||||||
response = APIClient().get(
|
|
||||||
"/api/v1.0/documents/collaboration-auth/",
|
|
||||||
HTTP_X_ORIGINAL_URL=f"http://localhost/ws/?invalid={document.pk}",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 403
|
|
||||||
assert "Authorization" not in response
|
|
||||||
assert "X-Can-Edit" not in response
|
|
||||||
assert "X-User-Id" not in response
|
|
||||||
|
|
||||||
|
|
||||||
def test_api_documents_collaboration_auth_secret_not_defined(settings):
|
|
||||||
"""
|
|
||||||
Trying to authenticate on the collaboration server when the secret is not defined
|
|
||||||
should return a 403.
|
|
||||||
"""
|
|
||||||
settings.COLLABORATION_SERVER_SECRET = None
|
|
||||||
|
|
||||||
document = factories.DocumentFactory(link_reach="public")
|
|
||||||
|
|
||||||
response = APIClient().get(
|
|
||||||
"/api/v1.0/documents/collaboration-auth/",
|
|
||||||
HTTP_X_ORIGINAL_URL=f"http://localhost/ws/?room={document.pk}",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 403
|
|
||||||
assert "Authorization" not in response
|
|
||||||
assert "X-Can-Edit" not in response
|
|
||||||
assert "X-User-Id" not in response
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(COLLABORATION_SERVER_SECRET="123")
|
|
||||||
@pytest.mark.parametrize("reach", ["authenticated", "restricted"])
|
|
||||||
def test_api_documents_collaboration_auth_anonymous_authenticated_or_restricted(reach):
|
|
||||||
"""
|
|
||||||
Anonymous users should not be allowed to connect to the collaboration server for a document
|
|
||||||
with link reach set to authenticated or restricted.
|
|
||||||
"""
|
|
||||||
document = factories.DocumentFactory(link_reach=reach)
|
|
||||||
|
|
||||||
response = APIClient().get(
|
|
||||||
"/api/v1.0/documents/collaboration-auth/",
|
|
||||||
HTTP_X_ORIGINAL_URL=f"http://localhost/ws/?room={document.pk}",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 403
|
|
||||||
assert "Authorization" not in response
|
|
||||||
assert "X-Can-Edit" not in response
|
|
||||||
assert "X-User-Id" not in response
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(COLLABORATION_SERVER_SECRET="123")
|
|
||||||
def test_api_documents_collaboration_auth_anonymous_public():
|
|
||||||
"""
|
|
||||||
Anonymous users should be able to connect to the collaboration server for a public document.
|
|
||||||
"""
|
|
||||||
document = factories.DocumentFactory(link_reach="public")
|
|
||||||
|
|
||||||
response = APIClient().get(
|
|
||||||
"/api/v1.0/documents/collaboration-auth/",
|
|
||||||
HTTP_X_ORIGINAL_URL=f"http://localhost/ws/?room={document.pk}",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response["Authorization"] == "123"
|
|
||||||
assert response["X-Can-Edit"] == str(document.link_role == "editor")
|
|
||||||
assert "X-User-Id" not in response
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(COLLABORATION_SERVER_SECRET="123")
|
|
||||||
@pytest.mark.parametrize("reach", ["public", "authenticated"])
|
|
||||||
def test_api_documents_collaboration_auth_authenticated_public_or_authenticated(reach):
|
|
||||||
"""
|
|
||||||
Authenticated users who are not related to a document should be able to connect to the
|
|
||||||
collaboration server if this document's link reach is set to public or authenticated.
|
|
||||||
"""
|
|
||||||
user = factories.UserFactory()
|
|
||||||
client = APIClient()
|
|
||||||
client.force_login(user)
|
|
||||||
|
|
||||||
document = factories.DocumentFactory(link_reach=reach)
|
|
||||||
|
|
||||||
response = client.get(
|
|
||||||
"/api/v1.0/documents/collaboration-auth/",
|
|
||||||
HTTP_X_ORIGINAL_URL=f"http://localhost/ws/?room={document.pk}",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response["Authorization"] == "123"
|
|
||||||
assert response["X-Can-Edit"] == str(document.link_role == "editor")
|
|
||||||
assert response["X-User-Id"] == str(user.id)
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(COLLABORATION_SERVER_SECRET="123")
|
|
||||||
def test_api_documents_collaboration_auth_authenticated_restricted():
|
|
||||||
"""
|
|
||||||
Authenticated users who are not related to a document should not be allowed to connect to the
|
|
||||||
collaboration server if this document's link reach is set to restricted.
|
|
||||||
"""
|
|
||||||
user = factories.UserFactory()
|
|
||||||
client = APIClient()
|
|
||||||
client.force_login(user)
|
|
||||||
|
|
||||||
document = factories.DocumentFactory(link_reach="restricted")
|
|
||||||
|
|
||||||
response = client.get(
|
|
||||||
"/api/v1.0/documents/collaboration-auth/",
|
|
||||||
HTTP_X_ORIGINAL_URL=f"http://localhost/ws/?room={document.pk}",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 403
|
|
||||||
assert "Authorization" not in response
|
|
||||||
assert "X-Can-Edit" not in response
|
|
||||||
assert "X-User-Id" not in response
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(COLLABORATION_SERVER_SECRET="123")
|
|
||||||
@pytest.mark.parametrize("role", models.RoleChoices.values)
|
|
||||||
@pytest.mark.parametrize("via", VIA)
|
|
||||||
def test_api_documents_collaboration_auth_related(via, role, mock_user_teams):
|
|
||||||
"""
|
|
||||||
Users who have a specific access to a document, whatever the role, should be able to
|
|
||||||
connect to the collaboration server for this document.
|
|
||||||
"""
|
|
||||||
user = factories.UserFactory()
|
|
||||||
client = APIClient()
|
|
||||||
client.force_login(user)
|
|
||||||
|
|
||||||
document = factories.DocumentFactory(link_reach="restricted")
|
|
||||||
if via == USER:
|
|
||||||
factories.UserDocumentAccessFactory(document=document, user=user, role=role)
|
|
||||||
elif via == TEAM:
|
|
||||||
mock_user_teams.return_value = ["lasuite", "unknown"]
|
|
||||||
factories.TeamDocumentAccessFactory(
|
|
||||||
document=document, team="lasuite", role=role
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get(
|
|
||||||
"/api/v1.0/documents/collaboration-auth/",
|
|
||||||
HTTP_X_ORIGINAL_URL=f"http://localhost/ws/?room={document.pk}",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response["Authorization"] == "123"
|
|
||||||
assert response["X-Can-Edit"] == str(role != "reader")
|
|
||||||
assert response["X-User-Id"] == str(user.id)
|
|
||||||
@@ -75,15 +75,11 @@ ingressCollaborationWS:
|
|||||||
## @param ingressCollaborationWS.customBackends Add custom backends to ingress
|
## @param ingressCollaborationWS.customBackends Add custom backends to ingress
|
||||||
customBackends: []
|
customBackends: []
|
||||||
|
|
||||||
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/auth-response-headers
|
|
||||||
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/auth-url
|
|
||||||
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/enable-websocket
|
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/enable-websocket
|
||||||
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-read-timeout
|
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-read-timeout
|
||||||
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-send-timeout
|
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/proxy-send-timeout
|
||||||
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/upstream-hash-by
|
## @param ingressCollaborationWS.annotations.nginx.ingress.kubernetes.io/upstream-hash-by
|
||||||
annotations:
|
annotations:
|
||||||
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization, X-Can-Edit, X-User-Id"
|
|
||||||
nginx.ingress.kubernetes.io/auth-url: https://impress.example.com/api/v1.0/documents/collaboration-auth/
|
|
||||||
nginx.ingress.kubernetes.io/enable-websocket: "true"
|
nginx.ingress.kubernetes.io/enable-websocket: "true"
|
||||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "86400"
|
nginx.ingress.kubernetes.io/proxy-read-timeout: "86400"
|
||||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "86400"
|
nginx.ingress.kubernetes.io/proxy-send-timeout: "86400"
|
||||||
|
|||||||
Reference in New Issue
Block a user