mirror of
https://github.com/suitenumerique/meet.git
synced 2026-09-03 06:08:29 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 089d016d12 | |||
| cf3960db95 | |||
| d80d31897c | |||
| 63a7751072 |
@@ -11,6 +11,7 @@ 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,9 +1076,10 @@ class RecordingViewSet(
|
|||||||
|
|
||||||
def _auth_get_original_url(self, request):
|
def _auth_get_original_url(self, request):
|
||||||
"""
|
"""
|
||||||
Extracts and parses the original URL from the "HTTP_X_ORIGINAL_URL" header.
|
Extracts and parses the original URL from the configured header.
|
||||||
Raises PermissionDenied if the header is missing.
|
Raises PermissionDenied if the header is missing.
|
||||||
The original url is passed by nginx in the "HTTP_X_ORIGINAL_URL" header.
|
The original url is passed by the reverse proxy in the header named by the
|
||||||
|
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.
|
||||||
@@ -1088,9 +1089,13 @@ 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("HTTP_X_ORIGINAL_URL")
|
original_url = request.META.get(settings.MEDIA_AUTH_ORIGINAL_URL_HEADER)
|
||||||
if not original_url:
|
if not original_url:
|
||||||
logger.warning("Missing HTTP_X_ORIGINAL_URL header in subrequest")
|
logger.warning(
|
||||||
|
"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)
|
||||||
@@ -1415,7 +1420,8 @@ 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 nginx in the "HTTP_X_ORIGINAL_URL" header.
|
The original url is passed by the reverse proxy in the header named by the
|
||||||
|
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.
|
||||||
@@ -1434,9 +1440,13 @@ 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("HTTP_X_ORIGINAL_URL")
|
original_url = request.META.get(settings.MEDIA_AUTH_ORIGINAL_URL_HEADER)
|
||||||
if not original_url:
|
if not original_url:
|
||||||
logger.warning("Missing HTTP_X_ORIGINAL_URL header in subrequest")
|
logger.warning(
|
||||||
|
"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,6 +7,7 @@ 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
|
||||||
@@ -143,3 +144,59 @@ 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,6 +8,7 @@ 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
|
||||||
@@ -282,3 +283,63 @@ 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,6 +129,15 @@ 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
|
||||||
|
|
||||||
|
|||||||
@@ -32,14 +32,18 @@ export interface BackgroundProcessorInterface extends TrackProcessor<Track.Kind>
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class BackgroundProcessorFactory {
|
export class BackgroundProcessorFactory {
|
||||||
|
private static _isSupported?: boolean
|
||||||
|
|
||||||
static hasModernApiSupport() {
|
static hasModernApiSupport() {
|
||||||
return ProcessorWrapper.hasModernApiSupport
|
return ProcessorWrapper.hasModernApiSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
static isSupported() {
|
static isSupported() {
|
||||||
return (
|
if (this._isSupported === undefined) {
|
||||||
supportsBackgroundProcessors() || BackgroundCustomProcessor.isSupported
|
this._isSupported =
|
||||||
)
|
supportsBackgroundProcessors() || BackgroundCustomProcessor.isSupported
|
||||||
|
}
|
||||||
|
return this._isSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
static getProcessor(
|
static getProcessor(
|
||||||
|
|||||||
Reference in New Issue
Block a user