Skip to content

Commit e697478

Browse files
committed
🚚(back) serve legacy shared live medias from Scaleway S3
Shared live medias are now served from the aws/ directory in Scaleway S3. They are served using the django-storage already in place. As the newer shared live medias are already stored in Scaleway, we do not rename these files and allow them to be served without requiring signed URLs.
1 parent 6615500 commit e697478

File tree

8 files changed

+249
-289
lines changed

8 files changed

+249
-289
lines changed

src/backend/marsha/core/models/video.py

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,44 +1261,6 @@ class Meta:
12611261
verbose_name = _("shared live media")
12621262
verbose_name_plural = _("shared live medias")
12631263

1264-
def get_source_s3_key(self, stamp=None, extension=None):
1265-
"""Compute the S3 key in the source bucket.
1266-
1267-
It is built from the video ID + ID of the shared live media + version stamp.
1268-
1269-
Parameters
1270-
----------
1271-
stamp: Type[string]
1272-
Passing a value for this argument will return the source S3 key for the shared live
1273-
media assuming its active stamp is set to this value. This is useful to create an
1274-
upload policy for this prospective version of the track, so that the client can
1275-
upload the file to S3 and the confirmation lambda can set the `uploaded_on` field
1276-
to this value only after the file upload and processing is successful.
1277-
1278-
1279-
extension: Type[string]
1280-
The extension used by the uploaded media. This extension is added at the end of the key
1281-
to keep a record of the extension. We will use it in the update-state endpoint to
1282-
record it in the database.
1283-
1284-
Returns
1285-
-------
1286-
string
1287-
The S3 key for the shared live media in the source bucket, where uploaded files are
1288-
stored before they are converted and copied to the destination bucket.
1289-
1290-
"""
1291-
# We don't want to deal with None value so we set it with an empty string
1292-
extension = extension or ""
1293-
1294-
# We check if the extension starts with a leading dot or not. If it's not the case we add
1295-
# it at the beginning of the string
1296-
if extension and not extension.startswith("."):
1297-
extension = "." + extension
1298-
1299-
stamp = stamp or self.uploaded_on_stamp()
1300-
return f"{self.video.pk}/sharedlivemedia/{self.pk}/{stamp}{extension}"
1301-
13021264
def get_storage_prefix(
13031265
self,
13041266
stamp=None,

src/backend/marsha/core/serializers/shared_live_media.py

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
"""Structure of SharedLiveMedia related models API responses with DRF serializers."""
22

3-
from urllib.parse import quote_plus
4-
5-
from django.conf import settings
6-
73
from rest_framework import serializers
84

9-
from marsha.core.defaults import CELERY_PIPELINE
5+
from marsha.core.defaults import AWS_STORAGE_BASE_DIRECTORY, CELERY_PIPELINE
106
from marsha.core.models import SharedLiveMedia
117
from marsha.core.serializers.base import (
128
TimestampField,
139
UploadableFileWithExtensionSerializerMixin,
14-
get_video_cloudfront_url_params,
1510
)
1611
from marsha.core.storage.storage_class import file_storage
17-
from marsha.core.utils import cloudfront_utils, time_utils
12+
from marsha.core.utils import time_utils
1813

1914

2015
class SharedLiveMediaSerializer(
@@ -100,33 +95,20 @@ def get_aws_pipeline_url(self, obj):
10095
urls = {}
10196

10297
stamp = time_utils.to_timestamp(obj.uploaded_on)
103-
base = (
104-
f"{settings.AWS_S3_URL_PROTOCOL}://{settings.CLOUDFRONT_DOMAIN}/"
105-
f"{obj.video_id}/sharedlivemedia/{obj.pk}/{stamp}"
106-
)
10798

108-
if settings.CLOUDFRONT_SIGNED_URLS_ACTIVE:
109-
params = get_video_cloudfront_url_params(obj.video_id)
99+
base = obj.get_storage_prefix(stamp=stamp, base_dir=AWS_STORAGE_BASE_DIRECTORY)
110100

111101
pages = {}
112102
for page_number in range(1, obj.nb_pages + 1):
113-
url = f"{base:s}_{page_number:d}.svg"
114-
if settings.CLOUDFRONT_SIGNED_URLS_ACTIVE:
115-
url = cloudfront_utils.build_signed_url(url, params)
116-
pages[page_number] = url
103+
url = f"{base}_{page_number}.svg"
104+
pages[page_number] = file_storage.url(url)
117105

118106
urls["pages"] = pages
119107

120-
# Downloadable link can be generated only when cloudfront request is signed
121-
if (
122-
self.context.get("is_admin") or obj.show_download
123-
) and settings.CLOUDFRONT_SIGNED_URLS_ACTIVE:
108+
if self.context.get("is_admin") or obj.show_download:
124109
extension = f".{obj.extension}" if obj.extension else ""
125-
urls["media"] = cloudfront_utils.build_signed_url(
126-
f"{base}{extension}?response-content-disposition="
127-
f"{quote_plus('attachment; filename=' + self.get_filename(obj))}",
128-
params,
129-
)
110+
url = f"{base}{extension}"
111+
urls["media"] = file_storage.url(url)
130112

131113
return urls
132114

src/backend/marsha/core/tests/api/shared_live_media/test_list.py

Lines changed: 47 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ def test_api_shared_live_media_list_student(self):
7272

7373
self.assertEqual(response.status_code, 403)
7474

75+
@override_settings(
76+
MEDIA_URL="https://abc.svc.edge.scw.cloud/",
77+
)
7578
def test_api_shared_live_media_list_instructor(self):
7679
"""An instructor can list shared live media details."""
7780
video = VideoFactory()
@@ -136,20 +139,24 @@ def test_api_shared_live_media_list_instructor(self):
136139
"title": "python expressions",
137140
"upload_state": "ready",
138141
"urls": {
142+
"media": (
143+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
144+
f"sharedlivemedia/{shared_live_media2.id}/1638230400.pdf"
145+
),
139146
"pages": {
140147
"1": (
141-
f"https://abc.cloudfront.net/{video.id}/"
148+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
142149
f"sharedlivemedia/{shared_live_media2.id}/1638230400_1.svg"
143150
),
144151
"2": (
145-
f"https://abc.cloudfront.net/{video.id}/"
152+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
146153
f"sharedlivemedia/{shared_live_media2.id}/1638230400_2.svg"
147154
),
148155
"3": (
149-
f"https://abc.cloudfront.net/{video.id}/"
156+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
150157
f"sharedlivemedia/{shared_live_media2.id}/1638230400_3.svg"
151158
),
152-
}
159+
},
153160
},
154161
"video": str(video.id),
155162
},
@@ -198,10 +205,9 @@ def test_api_shared_live_media_list_instructor_other_video(self):
198205
)
199206

200207
@override_settings(
201-
CLOUDFRONT_SIGNED_URLS_ACTIVE=True,
202-
CLOUDFRONT_SIGNED_PUBLIC_KEY_ID="cloudfront-access-key-id",
208+
MEDIA_URL="https://abc.svc.edge.scw.cloud/",
203209
)
204-
def test_api_shared_live_media_list_instructor_ready_to_show_and_signed_url_active(
210+
def test_api_shared_live_media_list_instructor_ready_to_show(
205211
self,
206212
):
207213
"""An instructor can list shared live media details ready to show and signed url on."""
@@ -236,8 +242,11 @@ def test_api_shared_live_media_list_instructor_ready_to_show_and_signed_url_acti
236242

237243
# fix the time so that the url signature is deterministic and can be checked
238244
now = datetime(2021, 11, 30, tzinfo=baseTimezone.utc)
239-
with mock.patch.object(timezone, "now", return_value=now), mock.patch(
240-
"builtins.open", new_callable=mock.mock_open, read_data=RSA_KEY_MOCK
245+
with (
246+
mock.patch.object(timezone, "now", return_value=now),
247+
mock.patch(
248+
"builtins.open", new_callable=mock.mock_open, read_data=RSA_KEY_MOCK
249+
),
241250
):
242251
response = self.client.get(
243252
self._get_url(video),
@@ -246,19 +255,6 @@ def test_api_shared_live_media_list_instructor_ready_to_show_and_signed_url_acti
246255

247256
self.assertEqual(response.status_code, 200)
248257

249-
expected_cloudfront_signature = (
250-
"Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0"
251-
"cHM6Ly9hYmMuY2xvdWRmcm9udC5uZXQvZDlkNzA0OWMtNWEzZi00MDcwLWE0OTQt"
252-
"ZTZiZjBiZDhiOWZiLyoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFX"
253-
"UzpFcG9jaFRpbWUiOjE2MzgyMzc2MDB9fX1dfQ__&Signature=IVWMFfS7WQVTK"
254-
"LZl~gKgGES~BS~wVLBIOncSE6yVgg9zIrEI1Epq3AVkOsI7z10dyjgInNbPviArn"
255-
"xmlV~DQeN-ykgEWmGy7aT4lRCx61oXuHFtNkq8Qx-we~UY87mZ4~UTqmM~JVuuLd"
256-
"uMiRQB-I3XKaRQGRlsok5yGu0RhvLcZntVFp6QgYui3WtGvxSs2LjW0IakR1qepS"
257-
"Dl9LXI-F2bgl9Vd1U9eapPBhhoD0okebXm7NGg9gUMLXlmUo-RvsrAzzEteKctPp"
258-
"0Xzkydk~tcnMkJs4jfbQxKrpyF~N9OuCRYCs68ONhHvypOYU3K-wQEoAFlERBLia"
259-
"OzDZUzlyA__&Key-Pair-Id=cloudfront-access-key-id"
260-
)
261-
262258
content = json.loads(response.content)
263259
self.assertEqual(
264260
content,
@@ -278,26 +274,21 @@ def test_api_shared_live_media_list_instructor_ready_to_show_and_signed_url_acti
278274
"upload_state": "ready",
279275
"urls": {
280276
"media": (
281-
"https://abc.cloudfront.net/d9d7049c-5a3f-4070-a494-e6bf0bd8b9fb/"
282-
"sharedlivemedia/7520c16b-5846-41ca-822b-52b446a96809/1638230400."
283-
"pdf?response-content-disposition=attachment%3B+filename%3Dpython"
284-
f"-expressions.pdf&{expected_cloudfront_signature}"
277+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
278+
f"sharedlivemedia/{shared_live_media.id}/1638230400.pdf"
285279
),
286280
"pages": {
287281
"1": (
288-
"https://abc.cloudfront.net/d9d7049c-5a3f-4070-a494-e6bf0bd8b9"
289-
"fb/sharedlivemedia/7520c16b-5846-41ca-822b-52b446a96809/16382"
290-
f"30400_1.svg?{expected_cloudfront_signature}"
282+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
283+
f"sharedlivemedia/{shared_live_media.id}/1638230400_1.svg"
291284
),
292285
"2": (
293-
"https://abc.cloudfront.net/d9d7049c-5a3f-4070-a494-e6bf0bd8b9"
294-
"fb/sharedlivemedia/7520c16b-5846-41ca-822b-52b446a96809/16382"
295-
f"30400_2.svg?{expected_cloudfront_signature}"
286+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
287+
f"sharedlivemedia/{shared_live_media.id}/1638230400_2.svg"
296288
),
297289
"3": (
298-
"https://abc.cloudfront.net/d9d7049c-5a3f-4070-a494-e6bf0bd8b9"
299-
"fb/sharedlivemedia/7520c16b-5846-41ca-822b-52b446a96809/16382"
300-
f"30400_3.svg?{expected_cloudfront_signature}"
290+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
291+
f"sharedlivemedia/{shared_live_media.id}/1638230400_3.svg"
301292
),
302293
},
303294
},
@@ -346,6 +337,9 @@ def test_api_shared_live_media_list_by_user_with_no_access(self):
346337

347338
self.assertEqual(response.status_code, 403)
348339

340+
@override_settings(
341+
MEDIA_URL="https://abc.svc.edge.scw.cloud/",
342+
)
349343
def test_api_shared_live_media_list_by_video_playlist_admin(self):
350344
"""
351345
Playlist administrator token user list shared live medias for a video.
@@ -418,17 +412,21 @@ def test_api_shared_live_media_list_by_video_playlist_admin(self):
418412
"title": "python extensions",
419413
"upload_state": "ready",
420414
"urls": {
415+
"media": (
416+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
417+
f"sharedlivemedia/{shared_live_media2.id}/1638230400.pdf"
418+
),
421419
"pages": {
422420
"1": (
423-
f"https://abc.cloudfront.net/{video.id}/"
421+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
424422
f"sharedlivemedia/{shared_live_media2.id}/1638230400_1.svg"
425423
),
426424
"2": (
427-
f"https://abc.cloudfront.net/{video.id}/"
425+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
428426
f"sharedlivemedia/{shared_live_media2.id}/1638230400_2.svg"
429427
),
430428
"3": (
431-
f"https://abc.cloudfront.net/{video.id}/"
429+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
432430
f"sharedlivemedia/{shared_live_media2.id}/1638230400_3.svg"
433431
),
434432
},
@@ -503,6 +501,9 @@ def test_api_shared_live_media_list_by_video_organization_instructor(self):
503501

504502
self.assertEqual(response.status_code, 403)
505503

504+
@override_settings(
505+
MEDIA_URL="https://abc.svc.edge.scw.cloud/",
506+
)
506507
def test_api_shared_live_media_list_by_video_organization_admin(self):
507508
"""
508509
Organization administrator token user list shared live medias for a video.
@@ -578,20 +579,24 @@ def test_api_shared_live_media_list_by_video_organization_admin(self):
578579
"title": "python extensions",
579580
"upload_state": "ready",
580581
"urls": {
582+
"media": (
583+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
584+
f"sharedlivemedia/{shared_live_media2.id}/1638230400.pdf"
585+
),
581586
"pages": {
582587
"1": (
583-
f"https://abc.cloudfront.net/{video.id}/"
588+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
584589
f"sharedlivemedia/{shared_live_media2.id}/1638230400_1.svg"
585590
),
586591
"2": (
587-
f"https://abc.cloudfront.net/{video.id}/"
592+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
588593
f"sharedlivemedia/{shared_live_media2.id}/1638230400_2.svg"
589594
),
590595
"3": (
591-
f"https://abc.cloudfront.net/{video.id}/"
596+
f"https://abc.svc.edge.scw.cloud/aws/{video.id}/"
592597
f"sharedlivemedia/{shared_live_media2.id}/1638230400_3.svg"
593598
),
594-
}
599+
},
595600
},
596601
"video": str(video.id),
597602
},

0 commit comments

Comments
 (0)