(back) create a cors proxy fetching docs external resources

When exporting a document in PDF and if the doc contains external
resources, we want to fetch them using a proxy bypassing CORS
restrictions. To ensure this endpoint is not used for something else
than fetching urls contains in the doc, we use access control and check
if the url really exists in the document.
This commit is contained in:
Anthony LC
2025-03-10 10:11:38 +01:00
committed by Anthony LC
parent 1c02b0ad8e
commit 6efc2377fe
6 changed files with 158 additions and 2 deletions

View File

@@ -0,0 +1,88 @@
"""Test on the CORS proxy API for documents."""
import pytest
from rest_framework.test import APIClient
from core import factories
pytestmark = pytest.mark.django_db
def test_api_docs_cors_proxy_valid_url():
"""Test the CORS proxy API for documents with a valid URL."""
document = factories.DocumentFactory(link_reach="public")
client = APIClient()
url_to_fetch = "https://docs.numerique.gouv.fr/assets/logo-gouv.png"
response = client.get(
f"/api/v1.0/documents/{document.id!s}/cors-proxy/?url={url_to_fetch}"
)
assert response.status_code == 200
assert response.headers["Content-Type"] == "image/png"
assert response.streaming_content
def test_api_docs_cors_proxy_without_url_query_string():
"""Test the CORS proxy API for documents without a URL query string."""
document = factories.DocumentFactory(link_reach="public")
client = APIClient()
response = client.get(f"/api/v1.0/documents/{document.id!s}/cors-proxy/")
assert response.status_code == 400
assert response.json() == {"detail": "Missing 'url' query parameter"}
def test_api_docs_cors_proxy_anonymous_document_not_public():
"""Test the CORS proxy API for documents with an anonymous user and a non-public document."""
document = factories.DocumentFactory(link_reach="authenticated")
client = APIClient()
url_to_fetch = "https://docs.numerique.gouv.fr/assets/logo-gouv.png"
response = client.get(
f"/api/v1.0/documents/{document.id!s}/cors-proxy/?url={url_to_fetch}"
)
assert response.status_code == 401
assert response.json() == {
"detail": "Authentication credentials were not provided."
}
def test_api_docs_cors_proxy_authenticated_user_accessing_protected_doc():
"""
Test the CORS proxy API for documents with an authenticated user accessing a protected
document.
"""
document = factories.DocumentFactory(link_reach="authenticated")
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
url_to_fetch = "https://docs.numerique.gouv.fr/assets/logo-gouv.png"
response = client.get(
f"/api/v1.0/documents/{document.id!s}/cors-proxy/?url={url_to_fetch}"
)
assert response.status_code == 200
assert response.headers["Content-Type"] == "image/png"
assert response.streaming_content
def test_api_docs_cors_proxy_authenticated_not_accessing_restricted_doc():
"""
Test the CORS proxy API for documents with an authenticated user not accessing a restricted
document.
"""
document = factories.DocumentFactory(link_reach="restricted")
user = factories.UserFactory()
client = APIClient()
client.force_login(user)
url_to_fetch = "https://docs.numerique.gouv.fr/assets/logo-gouv.png"
response = client.get(
f"/api/v1.0/documents/{document.id!s}/cors-proxy/?url={url_to_fetch}"
)
assert response.status_code == 403
assert response.json() == {
"detail": "You do not have permission to perform this action."
}

View File

@@ -34,6 +34,7 @@ def test_api_documents_retrieve_anonymous_public_standalone():
"children_create": False,
"children_list": True,
"collaboration_auth": True,
"cors_proxy": True,
"descendants": True,
"destroy": False,
# Anonymous user can't favorite a document even with read access
@@ -100,6 +101,7 @@ def test_api_documents_retrieve_anonymous_public_parent():
"children_list": True,
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"destroy": False,
# Anonymous user can't favorite a document even with read access
"favorite": False,
@@ -194,6 +196,7 @@ def test_api_documents_retrieve_authenticated_unrelated_public_or_authenticated(
"children_list": True,
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"destroy": False,
"favorite": True,
"invite_owner": False,
@@ -266,6 +269,7 @@ def test_api_documents_retrieve_authenticated_public_or_authenticated_parent(rea
"children_list": True,
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"destroy": False,
"favorite": True,
"invite_owner": False,
@@ -444,6 +448,7 @@ def test_api_documents_retrieve_authenticated_related_parent():
"children_list": True,
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"destroy": access.role == "owner",
"favorite": True,
"invite_owner": access.role == "owner",

View File

@@ -79,6 +79,7 @@ def test_api_documents_trashbin_format():
"children_list": True,
"collaboration_auth": True,
"descendants": True,
"cors_proxy": True,
"destroy": True,
"favorite": True,
"invite_owner": True,