Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/olympia/lib/settings_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,7 @@ def get_language_url_map():
'olympia.scanners.tasks.run_customs': {'queue': 'devhub'},
'olympia.scanners.tasks.run_narc_on_version': {'queue': 'devhub'},
'olympia.scanners.tasks.run_yara': {'queue': 'devhub'},
'olympia.versions.tasks.call_source_builder': {'queue': 'devhub'},
'olympia.versions.tasks.soft_block_versions': {'queue': 'devhub'},
# Crons.
'olympia.addons.tasks.update_addon_average_daily_users': {'queue': 'cron'},
Expand Down Expand Up @@ -1557,3 +1558,8 @@ def read_only_mode(env):
SWAGGER_SCHEMA_FILE = path('schema.yml')

SWAGGER_UI_ENABLED = env('SWAGGER_UI_ENABLED', default=False) or TARGET != 'production'

# Source builder settings.
SOURCE_BUILDER_API_URL = env('SOURCE_BUILDER_API_URL', default=None)
SOURCE_BUILDER_API_KEY = env('SOURCE_BUILDER_API_KEY', default='this-is-a-dummy-key')
SOURCE_BUILDER_API_TIMEOUT = 5 # seconds
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 4.2.26 on 2025-11-13 08:55

from django.db import migrations
from olympia.core.db.migrations import CreateWaffleSwitch


class Migration(migrations.Migration):

dependencies = [
('versions', '0050_remove_versionreviewerflags_needs_human_review_by_mad'),
]

operations = [CreateWaffleSwitch('enable-source-builder')]
5 changes: 5 additions & 0 deletions src/olympia/versions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,8 @@ def flag_if_sources_were_provided(self, user):
from olympia.activity.utils import log_and_notify
from olympia.reviewers.models import NeedsHumanReview

from .tasks import call_source_builder

if self.source:
# Add Activity Log, notifying staff, relevant reviewers and
# other authors of the add-on.
Expand All @@ -835,6 +837,9 @@ def flag_if_sources_were_provided(self, user):
reason = NeedsHumanReview.REASONS.PENDING_REJECTION_SOURCES_PROVIDED
NeedsHumanReview.objects.create(version=self, reason=reason)

if waffle.switch_is_active('enable-source-builder'):
call_source_builder.delay(version_pk=self.pk)

@classmethod
def transformer(cls, versions):
"""Attach all the compatible apps and the file to the versions."""
Expand Down
36 changes: 36 additions & 0 deletions src/olympia/versions/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
import os
import tempfile
from io import BytesIO
from urllib.parse import urljoin

from django.conf import settings
from django.db import transaction
from django.template import loader
from django.urls import reverse

import requests
from django_statsd.clients import statsd
from PIL import Image

Expand All @@ -24,6 +27,7 @@
from olympia.files.utils import get_background_images
from olympia.lib.crypto.tasks import duplicate_addon_version
from olympia.reviewers.models import NeedsHumanReview
from olympia.scanners.tasks import make_adapter_with_retry
from olympia.users.models import UserProfile
from olympia.users.utils import get_task_user
from olympia.versions.compare import VersionString
Expand Down Expand Up @@ -416,3 +420,35 @@ def soft_block_versions(version_ids, reason=REASON_VERSION_DELETED, **kw):
),
overwrite_block_metadata=False,
)


@task
def call_source_builder(version_pk):
log.info('Calling source builder API for Version %s', version_pk)

try:
version = Version.objects.get(pk=version_pk)

with requests.Session() as http:
adapter = make_adapter_with_retry()
http.mount('http://', adapter)
http.mount('https://', adapter)

json_payload = {
'addon_id': version.addon_id,
'version_id': version.id,
'download_source_url': urljoin(
settings.EXTERNAL_SITE_URL,
reverse('downloads.source', kwargs={'version_id': version.id}),
),
'license_slug': version.license.slug,
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an example of request's payload:

{
  "addon_id": 85,
  "version_id": 93,
  "download_source_url": "http://olympia.test/downloads/source/93",
  "license_slug": "MPL-2.0"
}

http.post(
url=settings.SOURCE_BUILDER_API_URL,
json=json_payload,
timeout=settings.SOURCE_BUILDER_API_TIMEOUT,
)
except Exception:
log.exception(
'Error while calling source builder API for Version %s.', version_pk
)
16 changes: 16 additions & 0 deletions src/olympia/versions/views.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import os

from django import http
from django.conf import settings
from django.db.transaction import non_atomic_requests
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.urls import reverse
from django.utils.cache import patch_cache_control, patch_vary_headers
from django.utils.crypto import constant_time_compare

import waffle

import olympia.core.logger
from olympia import amo
Expand Down Expand Up @@ -218,6 +222,18 @@ def download_source(request, version_id):
allow_addons_edit_permission=False,
allow_developer=True,
)

# Source code can also be downloaded using an API key when the source
# builder feature is enabled.
api_key = request.headers.get('x-api-key')
if waffle.switch_is_active('enable-source-builder') and api_key:
has_permission = constant_time_compare(api_key, settings.SOURCE_BUILDER_API_KEY)
if has_permission:
log.info(
'Source code for Version %s was accessed via SOURCE_BUILDER_API_KEY',
version.id,
)

if not (has_permission and getattr(version, 'source', None)):
raise http.Http404()

Expand Down
Loading