mirror of
https://github.com/suitenumerique/meet.git
synced 2026-09-02 21:58:29 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6265953858 | |||
| 0edac91793 |
@@ -11,7 +11,6 @@ and this project adheres to
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- ✨(frontend) add 1080p sending resolution option #1660
|
- ✨(frontend) add 1080p sending resolution option #1660
|
||||||
- ✨(backend) add Traefik support via configurable media-auth url header #1649
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -1076,10 +1076,9 @@ class RecordingViewSet(
|
|||||||
|
|
||||||
def _auth_get_original_url(self, request):
|
def _auth_get_original_url(self, request):
|
||||||
"""
|
"""
|
||||||
Extracts and parses the original URL from the configured header.
|
Extracts and parses the original URL from the "HTTP_X_ORIGINAL_URL" header.
|
||||||
Raises PermissionDenied if the header is missing.
|
Raises PermissionDenied if the header is missing.
|
||||||
The original url is passed by the reverse proxy in the header named by the
|
The original url is passed by nginx in the "HTTP_X_ORIGINAL_URL" header.
|
||||||
MEDIA_AUTH_ORIGINAL_URL_HEADER setting, which defaults to "HTTP_X_ORIGINAL_URL".
|
|
||||||
See corresponding ingress configuration in Helm chart and read about the
|
See corresponding ingress configuration in Helm chart and read about the
|
||||||
nginx.ingress.kubernetes.io/auth-url annotation to understand how the Nginx ingress
|
nginx.ingress.kubernetes.io/auth-url annotation to understand how the Nginx ingress
|
||||||
is configured to do this.
|
is configured to do this.
|
||||||
@@ -1089,13 +1088,9 @@ class RecordingViewSet(
|
|||||||
reasons.
|
reasons.
|
||||||
"""
|
"""
|
||||||
# Extract the original URL from the request header
|
# Extract the original URL from the request header
|
||||||
original_url = request.META.get(settings.MEDIA_AUTH_ORIGINAL_URL_HEADER)
|
original_url = request.META.get("HTTP_X_ORIGINAL_URL")
|
||||||
if not original_url:
|
if not original_url:
|
||||||
logger.warning(
|
logger.warning("Missing HTTP_X_ORIGINAL_URL header in subrequest")
|
||||||
"Missing %s header in subrequest. Set MEDIA_AUTH_ORIGINAL_URL_HEADER "
|
|
||||||
"to the header your reverse proxy sends.",
|
|
||||||
settings.MEDIA_AUTH_ORIGINAL_URL_HEADER,
|
|
||||||
)
|
|
||||||
raise drf_exceptions.PermissionDenied()
|
raise drf_exceptions.PermissionDenied()
|
||||||
|
|
||||||
logger.debug("Original url: '%s'", original_url)
|
logger.debug("Original url: '%s'", original_url)
|
||||||
@@ -1420,8 +1415,7 @@ class FileViewSet(
|
|||||||
Authorize access based on the original URL of an Nginx subrequest
|
Authorize access based on the original URL of an Nginx subrequest
|
||||||
and user permissions. Returns a dictionary of URL parameters if authorized.
|
and user permissions. Returns a dictionary of URL parameters if authorized.
|
||||||
|
|
||||||
The original url is passed by the reverse proxy in the header named by the
|
The original url is passed by nginx in the "HTTP_X_ORIGINAL_URL" header.
|
||||||
MEDIA_AUTH_ORIGINAL_URL_HEADER setting, which defaults to "HTTP_X_ORIGINAL_URL".
|
|
||||||
See corresponding ingress configuration in Helm chart and read about the
|
See corresponding ingress configuration in Helm chart and read about the
|
||||||
nginx.ingress.kubernetes.io/auth-url annotation to understand how the Nginx ingress
|
nginx.ingress.kubernetes.io/auth-url annotation to understand how the Nginx ingress
|
||||||
is configured to do this.
|
is configured to do this.
|
||||||
@@ -1440,13 +1434,9 @@ class FileViewSet(
|
|||||||
- PermissionDenied if authorization fails.
|
- PermissionDenied if authorization fails.
|
||||||
"""
|
"""
|
||||||
# Extract the original URL from the request header
|
# Extract the original URL from the request header
|
||||||
original_url = request.META.get(settings.MEDIA_AUTH_ORIGINAL_URL_HEADER)
|
original_url = request.META.get("HTTP_X_ORIGINAL_URL")
|
||||||
if not original_url:
|
if not original_url:
|
||||||
logger.warning(
|
logger.warning("Missing HTTP_X_ORIGINAL_URL header in subrequest")
|
||||||
"Missing %s header in subrequest. Set MEDIA_AUTH_ORIGINAL_URL_HEADER "
|
|
||||||
"to the header your reverse proxy sends.",
|
|
||||||
settings.MEDIA_AUTH_ORIGINAL_URL_HEADER,
|
|
||||||
)
|
|
||||||
raise drf_exceptions.PermissionDenied()
|
raise drf_exceptions.PermissionDenied()
|
||||||
|
|
||||||
parsed_url = urlparse(original_url)
|
parsed_url = urlparse(original_url)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from urllib.parse import quote, urlparse
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.test import override_settings
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -144,59 +143,3 @@ def test_api_files_media_auth_own_file_deleted():
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == 403
|
assert response.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
@override_settings(MEDIA_AUTH_ORIGINAL_URL_HEADER="HTTP_X_FORWARDED_URI")
|
|
||||||
def test_api_files_media_auth_custom_original_url_header():
|
|
||||||
"""
|
|
||||||
Authorization should honour the configured original-url header.
|
|
||||||
|
|
||||||
Covers the attachment subrequest path, which resolves the header separately
|
|
||||||
from the recording one. Reverse proxies other than nginx-ingress use
|
|
||||||
different headers: Traefik's ForwardAuth sends X-Forwarded-Uri and cannot
|
|
||||||
emit X-Original-URL at all.
|
|
||||||
"""
|
|
||||||
user = factories.UserFactory()
|
|
||||||
|
|
||||||
file = factories.FileFactory(
|
|
||||||
type=models.FileTypeChoices.BACKGROUND_IMAGE,
|
|
||||||
update_upload_state=models.FileUploadStateChoices.READY,
|
|
||||||
creator=user,
|
|
||||||
)
|
|
||||||
|
|
||||||
client = APIClient()
|
|
||||||
client.force_login(user)
|
|
||||||
|
|
||||||
default_storage.save(file.file_key, BytesIO(b"my prose"))
|
|
||||||
|
|
||||||
original_url = f"http://localhost/media/{file.file_key:s}"
|
|
||||||
response = client.get(
|
|
||||||
"/api/v1.0/files/media-auth/", HTTP_X_FORWARDED_URI=original_url
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert "AWS4-HMAC-SHA256 Credential=" in response["Authorization"]
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(MEDIA_AUTH_ORIGINAL_URL_HEADER="HTTP_X_FORWARDED_URI")
|
|
||||||
def test_api_files_media_auth_default_header_ignored_when_reconfigured():
|
|
||||||
"""
|
|
||||||
Only the configured header should be honoured, never a hardcoded fallback.
|
|
||||||
"""
|
|
||||||
user = factories.UserFactory()
|
|
||||||
|
|
||||||
file = factories.FileFactory(
|
|
||||||
type=models.FileTypeChoices.BACKGROUND_IMAGE,
|
|
||||||
update_upload_state=models.FileUploadStateChoices.READY,
|
|
||||||
creator=user,
|
|
||||||
)
|
|
||||||
|
|
||||||
client = APIClient()
|
|
||||||
client.force_login(user)
|
|
||||||
|
|
||||||
original_url = f"http://localhost/media/{file.file_key:s}"
|
|
||||||
response = client.get(
|
|
||||||
"/api/v1.0/files/media-auth/", HTTP_X_ORIGINAL_URL=original_url
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 403
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from uuid import uuid4
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.files.storage import default_storage
|
from django.core.files.storage import default_storage
|
||||||
from django.test import override_settings
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -283,63 +282,3 @@ def test_api_recordings_media_auth_success_administrator(mode):
|
|||||||
timeout=1,
|
timeout=1,
|
||||||
)
|
)
|
||||||
assert response.content.decode("utf-8") == "my prose"
|
assert response.content.decode("utf-8") == "my prose"
|
||||||
|
|
||||||
|
|
||||||
def test_api_recordings_media_auth_missing_header():
|
|
||||||
"""
|
|
||||||
Test that a subrequest without the configured original-url header is rejected.
|
|
||||||
"""
|
|
||||||
user = UserFactory()
|
|
||||||
|
|
||||||
client = APIClient()
|
|
||||||
client.force_login(user)
|
|
||||||
|
|
||||||
response = client.get("/api/v1.0/recordings/media-auth/")
|
|
||||||
|
|
||||||
assert response.status_code == 403
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(MEDIA_AUTH_ORIGINAL_URL_HEADER="HTTP_X_FORWARDED_URI")
|
|
||||||
def test_api_recordings_media_auth_custom_original_url_header():
|
|
||||||
"""
|
|
||||||
Test that the header carrying the original URL can be configured.
|
|
||||||
|
|
||||||
Reverse proxies other than nginx-ingress use different headers: Traefik's
|
|
||||||
ForwardAuth sends X-Forwarded-Uri and cannot emit X-Original-URL at all.
|
|
||||||
"""
|
|
||||||
user = UserFactory()
|
|
||||||
|
|
||||||
client = APIClient()
|
|
||||||
client.force_login(user)
|
|
||||||
|
|
||||||
original_url = f"http://localhost/media/recordings/{uuid4()!s}.mp4"
|
|
||||||
|
|
||||||
response = client.get(
|
|
||||||
"/api/v1.0/recordings/media-auth/", HTTP_X_FORWARDED_URI=original_url
|
|
||||||
)
|
|
||||||
|
|
||||||
# The header was read and parsed: we get as far as looking the recording up,
|
|
||||||
# rather than being rejected for a missing header.
|
|
||||||
assert response.status_code == 404
|
|
||||||
|
|
||||||
|
|
||||||
@override_settings(MEDIA_AUTH_ORIGINAL_URL_HEADER="HTTP_X_FORWARDED_URI")
|
|
||||||
def test_api_recordings_media_auth_default_header_ignored_when_reconfigured():
|
|
||||||
"""
|
|
||||||
Test that only the configured header is honoured.
|
|
||||||
|
|
||||||
Guards against the header being read from a hardcoded name in parallel with
|
|
||||||
the setting.
|
|
||||||
"""
|
|
||||||
user = UserFactory()
|
|
||||||
|
|
||||||
client = APIClient()
|
|
||||||
client.force_login(user)
|
|
||||||
|
|
||||||
original_url = f"http://localhost/media/recordings/{uuid4()!s}.mp4"
|
|
||||||
|
|
||||||
response = client.get(
|
|
||||||
"/api/v1.0/recordings/media-auth/", HTTP_X_ORIGINAL_URL=original_url
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 403
|
|
||||||
|
|||||||
@@ -129,15 +129,6 @@ class Base(Configuration):
|
|||||||
MEDIA_BASE_URL = values.Value(
|
MEDIA_BASE_URL = values.Value(
|
||||||
"", environ_name="MEDIA_BASE_URL", environ_prefix=None
|
"", environ_name="MEDIA_BASE_URL", environ_prefix=None
|
||||||
)
|
)
|
||||||
# Header the reverse proxy uses to pass the original request URL to the
|
|
||||||
# media-auth subrequest views. nginx-ingress sends X-Original-URL, which is
|
|
||||||
# the default. Other proxies use different headers -- Traefik's ForwardAuth,
|
|
||||||
# for instance, sends X-Forwarded-Uri and cannot emit X-Original-URL at all.
|
|
||||||
MEDIA_AUTH_ORIGINAL_URL_HEADER = values.Value(
|
|
||||||
default="HTTP_X_ORIGINAL_URL",
|
|
||||||
environ_name="MEDIA_AUTH_ORIGINAL_URL_HEADER",
|
|
||||||
environ_prefix=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user