diff --git a/docs/topics/api/index.rst b/docs/topics/api/index.rst index ed76b8c4e7de..1db59a4c1a69 100644 --- a/docs/topics/api/index.rst +++ b/docs/topics/api/index.rst @@ -47,5 +47,4 @@ using the API. reviewers scanners shelves - signing tags diff --git a/docs/topics/api/overview.rst b/docs/topics/api/overview.rst index 23e1bde1657c..eb3cea94dd4c 100644 --- a/docs/topics/api/overview.rst +++ b/docs/topics/api/overview.rst @@ -459,6 +459,7 @@ These are `v5` specific changes - `v4` changes apply also. * 2023-03-09: added ``is_disabled`` to version detail and update endpoints, for authenticated developers and revieweers. https://github.com/mozilla/addons-server/issues/20142 * 2023-03-08: restricted ``lang`` parameter to only alphanumeric, ``_``, ``-``. https://github.com/mozilla/addons-server/issues/20452 * 2023-03-09: added ``host_permissions`` to the response of the version detail endpoint. https://github.com/mozilla/addons-server/issues/20418 +* 2023-04-13: removed signing api from api/v5+ in favor of addon submission api. https://github.com/mozilla/addons-server/issues/20560 .. _`#11380`: https://github.com/mozilla/addons-server/issues/11380/ .. _`#11379`: https://github.com/mozilla/addons-server/issues/11379/ diff --git a/docs/topics/api/signing.rst b/docs/topics/api/signing.rst deleted file mode 100644 index 54220e8a155a..000000000000 --- a/docs/topics/api/signing.rst +++ /dev/null @@ -1,252 +0,0 @@ -======= -Signing -======= - -.. note:: - - These APIs are not frozen and can change at any time without warning. - See :ref:`the API versions available` for alternatives - if you need stability. - -The following API endpoints help you get your add-on signed by Mozilla -so it can be installed into Firefox without error. See -`extension signing `_ -for more details about Firefox's signing policy. - ----------------- -Client Libraries ----------------- - -The following libraries will make it easier to use the signing API: - -* `sign-addon `_, for general programattic use in - `NodeJS `_ -* `web-ext sign `_, - for developing `Web Extensions `_ - -If you are using ``curl`` to interact with the API you should be sure to pass -the ``-g`` flag to skip "URL globbing" which won't interact well with add-on -Ids that have {} characters in them. - - -.. _upload-version: - -------------------- -Uploading a version -------------------- - -You can upload a new version for signing by issuing a ``PUT`` request -and including the contents of your add-on in the ``upload`` parameter -as multi-part formdata. This will create a pending version on the -add-on and will prevent future submissions to this version unless -validation or review fails. - -If the upload succeeded then it will be submitted for -validation and you will be able to check its status. - -.. http:put:: /api/v5/addons/(string:guid)/versions/(string:version)/ - - **Request:** - - .. sourcecode:: bash - - curl "https://addons.mozilla.org/api/v5/addons/@my-addon/versions/1.0/" - -g -XPUT --form "upload=@build/my-addon.xpi" - -H "Authorization: JWT " - - :param guid: The add-on `extension identifier `_. - :param version: The version of the add-on. - :form upload: The add-on file being uploaded. - :form channel: (optional) The channel this version should be uploaded to, - which determines its visibility on the site. It can be either - ``unlisted`` or ``listed``. See the note below. - :reqheader Content-Type: multipart/form-data - - .. note:: - ``channel`` is only valid for new versions on existing add-ons. - If the add-on is new then the version will be created as ``unlisted``. - If the parameter isn't supplied then the channel of the most recent - version (submitted either via this API or the website) will be assumed. - For example, if you submit a version as ``listed`` then the next version - will be ``listed`` if you don't specify the channel. - - **Response:** - - The response body will be the same as the :ref:`version-status` response. - - :statuscode 201: new add-on and version created. - :statuscode 202: new version created. - :statuscode 400: an error occurred, check the `error` value in the JSON. - :statuscode 401: authentication failed. - :statuscode 403: you do not own this add-on. - :statuscode 409: version already exists. - - -Uploading without an ID ------------------------ - -.. note:: - This is only valid for `WebExtensions `_. - All other add-on types require an add-on ID and have to use the regular - endpoint to :ref:`upload a version `. - - -.. http:post:: /api/v5/addons/ - - **Request:** - - .. sourcecode:: bash - - curl "https://addons.mozilla.org/api/v5/addons/" - -g -XPOST -F "upload=@build/my-addon.xpi" -F "version=1.0" - -H "Authorization: JWT " - - :form upload: The add-on file being uploaded. - :form version: The version of the add-on. - :reqheader Content-Type: multipart/form-data - - **Response:** - - The response body will be the same as the :ref:`version-status` response. - - :statuscode 201: new add-on and version created. - :statuscode 202: new version created. - :statuscode 400: an error occurred, check the `error` value in the JSON. - :statuscode 401: authentication failed. - :statuscode 403: you do not own this add-on. - :statuscode 409: version already exists. - ------------------- -Creating an add-on ------------------- - -If this is the first time that your add-on's UUID has been seen then -the add-on will be created as an unlisted add-on when the version is -uploaded. - -.. _`version-status`: - ------------------------------------ -Checking the status of your upload ------------------------------------ - -You can check the status of your upload by issuing a ``GET`` request. -There are a few things that will happen once a version is uploaded -and the status of those events is included in the response. - -Once validation is completed (whether it passes or fails) then the -``processed`` property will be ``true``. You can check if validation -passed using the ``valid`` property and check the results with -``validation_results``. - -If validation passed then your add-on will be submitted for automated or -manual review. Once review is complete then then ``reviewed`` property will be -set and you can check the results with the ``passed_review`` property. - -.. http:get:: /api/v5/addons/(string:guid)/versions/(string:version)/[uploads/(string:upload-pk)/] - - **Request:** - - .. sourcecode:: bash - - curl "https://addons.mozilla.org/api/v5/addons/@my-addon/versions/1.0/" - -g -H "Authorization: JWT " - - :param guid: The add-on `extension identifier `_. - :param version: the version of the add-on. - :param upload-pk: (optional) the pk for a specific upload. - - **Response:** - - .. code-block:: json - - { - "guid": "420854ee-7a85-42b9-822f-8e03dc5f6de9", - "active": true, - "automated_signing": true, - "files": [ - { - "download_url": "https://addons.mozilla.org/api/v5/downloads/file/100/example-id.0-fx+an.xpi", - "hash": "sha256:1bb945266bf370170a656350d9b640cbcaf70e671cf753c410e604219cdd9267", - "signed": true - } - ], - "passed_review": true, - "pk": "f68abbb3b1624c098fe979a409fe3ce9", - "processed": true, - "reviewed": true, - "url": "https://addons.mozilla.org/api/v5/addons/@example-id.0/uploads/f68abbb3b1624c098fe979a409fe3ce9/", - "valid": true, - "validation_results": {}, - "validation_url": "https://addons.mozilla.org/en-US/developers/upload/f68abbb3b1624c098fe979a409fe3ce9", - "version": "1.0" - } - - :>json guid: The GUID of the addon. - :>json active: version is active. - :>json automated_signing: - If true, the version will be signed automatically. If false it will end - up in the manual review queue when valid. - :>json files[].download_url: - URL to :ref:`download the add-on file `. - :>json files[].hash: - Hash of the file contents, prefixed by the hashing algorithm used. - Example: ``sha256:1bb945266bf3701...`` . In the case of signed files, - the hash will be that of the final signed file, not the original - unsigned file. - :>json files[].signed: if the file is signed. - :>json passed_review: if the version has passed review. - :>json pk: the pk for this upload. - :>json processed: if the version has been processed by the validator. - :>json reviewed: if the version has been reviewed. - :>json url: URL to check the status of this upload. - :>json valid: if the version passed validation. - :>json validation_results: the validation results (removed from the example for brevity). - :>json validation_url: a URL to the validation results in HTML format. - :>json version: the version. - - :statuscode 200: request successful. - :statuscode 401: authentication failed. - :statuscode 403: you do not own this add-on. - :statuscode 404: add-on or version not found. - -.. _download-signed-file: - ------------------------- -Downloading signed files ------------------------- - -When checking on your :ref:`request to sign a version `, -a successful response will give you an API URL to download the signed files. -This endpoint returns the actual file data for download. - -.. http:get:: /api/v5/file/[int:file_id]/[string:base_filename] - - **Request:** - - .. sourcecode:: bash - - curl "https://addons.mozilla.org/api/v5/file/123/some-addon.xpi" - -g -H "Authorization: JWT " - - :param file_id: the primary key of the add-on file. - :param base_filename: - the base filename. This is just a convenience for - clients so that they write meaningful file names to disk. - - **Response:** - - There are two possible responses: - - * Binary data containing the file - * A header that redirects you to a mirror URL for the file. - In this case, the initial response will include a - ``SHA-256`` hash of the file in the header ``X-Target-Digest``. - Clients should check that the final downloaded file matches - this hash. - - :statuscode 200: request successful. - :statuscode 302: file resides at a mirror URL - :statuscode 401: authentication failed. - :statuscode 404: file does not exist or requester does not have - access to it. diff --git a/docs/topics/api/v4_frozen/accounts.rst b/docs/topics/api/v4_frozen/accounts.rst index 2aa8a4cbdffc..fc6ddc6acdb5 100644 --- a/docs/topics/api/v4_frozen/accounts.rst +++ b/docs/topics/api/v4_frozen/accounts.rst @@ -17,10 +17,10 @@ Account .. _v4-account: This endpoint returns information about a user's account, by the account id. -Only :ref:`developer ` accounts are publicly viewable - other user's accounts will return a 404 not found response code. +Only :ref:`developer ` accounts are publicly viewable - other user's accounts will return a 404 not found response code. Most of the information is optional and provided by the user so may be missing or inaccurate. -.. _`developer_account`: +.. _`v4-developer_account`: A developer is defined as a user who is listed as a developer or owner of one or more approved add-ons. @@ -227,7 +227,7 @@ Any number of notifications can be changed; only non-mandatory notifications can .. _v4-notification-update-request: - :: Is the notification enabled? + :json string edit_url: The URL to the developer edit page for the add-on. :>json string guid: The add-on `extension identifier `_. :>json boolean has_eula: The add-on has an End-User License Agreement that the user needs to agree with before installing (See :ref:`add-on EULA and privacy policy `). - :>json boolean has_privacy_policy: The add-on has a Privacy Policy (See :ref:`v4-add-on EULA and privacy policy `). + :>json boolean has_privacy_policy: The add-on has a Privacy Policy (See :ref:`add-on EULA and privacy policy `). :>json string|object|null homepage: The add-on homepage (See :ref:`translated fields ` and :ref:`Outgoing Links `). :>json string icon_url: The URL to icon for the add-on (including a cachebusting query string). :>json object icons: An object holding the URLs to an add-ons icon including a cachebusting query string as values and their size as properties. Currently exposes 32, 64, 128 pixels wide icons. @@ -449,7 +449,7 @@ on AMO. :>json string results[].guid: The add-on `extension identifier `_. :>json string results[].slug: The add-on slug. :>json string results[].target_locale: For dictionaries and language packs, the locale the add-on is meant for. Only present when using the Language Tools endpoint. - :>json string results[].type: The :ref:`v4-add-on type `. + :>json string results[].type: The :ref:`add-on type `. :>json string results[].url: The (absolute) add-on detail URL. diff --git a/docs/topics/api/v4_frozen/applications.rst b/docs/topics/api/v4_frozen/applications.rst index 82f4aaab61a8..9da14b0baa16 100644 --- a/docs/topics/api/v4_frozen/applications.rst +++ b/docs/topics/api/v4_frozen/applications.rst @@ -9,7 +9,7 @@ Applications Versions .. _v4-applications-version: This internal endpoint allows you to create applications versions to be -referenced in add-ons manifests. It requires :ref:`v4-authentication` +referenced in add-ons manifests. It requires :doc:`authentication` and a special permission. The currently available applications versions are :ref:`available to list`. diff --git a/docs/topics/api/v4_frozen/signing.rst b/docs/topics/api/v4_frozen/signing.rst index 3af15937b400..5cceae45b56a 100644 --- a/docs/topics/api/v4_frozen/signing.rst +++ b/docs/topics/api/v4_frozen/signing.rst @@ -107,7 +107,7 @@ Uploading without an ID **Response:** - The response body will be the same as the :ref:`version-status` response. + The response body will be the same as the :ref:`v4-version-status` response. :statuscode 201: new add-on and version created. :statuscode 202: new version created. @@ -188,7 +188,7 @@ set and you can check the results with the ``passed_review`` property. If true, the version will be signed automatically. If false it will end up in the manual review queue when valid. :>json files[].download_url: - URL to :ref:`download the add-on file `. + URL to :ref:`download the add-on file `. :>json files[].hash: Hash of the file contents, prefixed by the hashing algorithm used. Example: ``sha256:1bb945266bf3701...`` . In the case of signed files, @@ -216,7 +216,7 @@ set and you can check the results with the ``passed_review`` property. Downloading signed files ------------------------ -When checking on your :ref:`request to sign a version `, +When checking on your :ref:`request to sign a version `, a successful response will give you an API URL to download the signed files. This endpoint returns the actual file data for download. diff --git a/src/olympia/api/urls.py b/src/olympia/api/urls.py index 16e002ebd9e7..b6591b3f4d97 100644 --- a/src/olympia/api/urls.py +++ b/src/olympia/api/urls.py @@ -44,7 +44,6 @@ re_path(r'^hero/', include('olympia.hero.urls')), re_path(r'^ratings/', include(ratings_v4.urls)), re_path(r'^reviewers/', include('olympia.reviewers.api_urls')), - re_path(r'^', include('olympia.signing.urls')), re_path(r'^', include(amo_api_patterns)), re_path(r'^scanner/', include('olympia.scanners.api_urls')), re_path(r'^shelves/', include('olympia.shelves.urls')), diff --git a/src/olympia/devhub/forms.py b/src/olympia/devhub/forms.py index e79951bd96d2..26ab53b0542c 100644 --- a/src/olympia/devhub/forms.py +++ b/src/olympia/devhub/forms.py @@ -40,6 +40,7 @@ validate_version_number_is_gt_latest_signed_listed_version, verify_mozilla_trademark, ) +from olympia.addons.views import AddonViewSet from olympia.amo.fields import HttpHttpsOnlyURLField, ReCaptchaField from olympia.amo.forms import AMOModelForm from olympia.amo.messages import DoubleSafe @@ -966,9 +967,7 @@ def check_throttles(self, request): Raises ValidationError if the request is throttled. """ - from olympia.signing.views import VersionView # circular import - - view = VersionView() + view = AddonViewSet() try: view.check_throttles(request) except Throttled: diff --git a/src/olympia/devhub/tests/test_forms.py b/src/olympia/devhub/tests/test_forms.py index 0aebe3f7840d..81c673ea0b80 100644 --- a/src/olympia/devhub/tests/test_forms.py +++ b/src/olympia/devhub/tests/test_forms.py @@ -16,6 +16,7 @@ from olympia import amo, core from olympia.addons.models import Addon +from olympia.addons.views import AddonViewSet from olympia.amo.tests import ( addon_factory, get_random_ip, @@ -28,7 +29,6 @@ from olympia.applications.models import AppVersion from olympia.devhub import forms from olympia.files.models import FileUpload -from olympia.signing.views import VersionView from olympia.tags.models import AddonTag, Tag from olympia.versions.models import ApplicationsVersions @@ -221,7 +221,7 @@ def test_throttling(self, parse_addon_mock): with freeze_time('2019-04-08 15:16:23.42') as frozen_time: for x in range(0, 6): self._add_fake_throttling_action( - view_class=VersionView, + view_class=AddonViewSet, url='/', user=request.user, remote_addr=get_random_ip(), diff --git a/src/olympia/signing/tests/__init__.py b/src/olympia/signing/tests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/olympia/signing/tests/test_views.py b/src/olympia/signing/tests/test_views.py index 681300f06dbe..91c3a50fe7bc 100644 --- a/src/olympia/signing/tests/test_views.py +++ b/src/olympia/signing/tests/test_views.py @@ -12,6 +12,7 @@ import responses from freezegun import freeze_time +from rest_framework.response import Response from olympia import amo from olympia.access.models import Group, GroupUser @@ -31,7 +32,6 @@ from olympia.blocklist.models import Block from olympia.files.models import File, FileUpload from olympia.files.utils import get_sha256 -from olympia.signing.views import VersionView from olympia.users.models import ( EmailUserRestriction, IPNetworkUserRestriction, @@ -39,7 +39,8 @@ UserRestrictionHistory, ) from olympia.versions.models import Version -from rest_framework.response import Response + +from ..views import VersionView class SigningAPITestMixin(APIKeyAuthTestMixin): @@ -73,7 +74,7 @@ def url(self, guid, version, pk=None): args = [guid, version] if pk is not None: args.append(pk) - return reverse_ns('signing.version', args=args) + return reverse_ns('signing.version', api_version='v4', args=args) def create_version(self, version): response = self.request('PUT', self.url(self.guid, version), version) @@ -552,7 +553,7 @@ def test_invalid_guid_in_package_post(self): response = self.request( 'POST', - url=reverse_ns('signing.version'), + url=reverse_ns('signing.version', api_version='v4'), version='1.0', filename='src/olympia/files/fixtures/files/invalid_guid.xpi', ) @@ -825,23 +826,23 @@ def _test_throttling_verb_user_daily(self, verb, url, expected_status=201): assert response.status_code == expected_status def test_throttling_post_ip_burst(self): - url = reverse_ns('signing.version') + url = reverse_ns('signing.version', api_version='v4') self._test_throttling_verb_ip_burst('POST', url) def test_throttling_post_ip_hourly(self): - url = reverse_ns('signing.version') + url = reverse_ns('signing.version', api_version='v4') self._test_throttling_verb_ip_hourly('POST', url) def test_throttling_post_user_burst(self): - url = reverse_ns('signing.version') + url = reverse_ns('signing.version', api_version='v4') self._test_throttling_verb_user_burst('POST', url) def test_throttling_post_user_hourly(self): - url = reverse_ns('signing.version') + url = reverse_ns('signing.version', api_version='v4') self._test_throttling_verb_user_hourly('POST', url) def test_throttling_post_user_daily(self): - url = reverse_ns('signing.version') + url = reverse_ns('signing.version', api_version='v4') self._test_throttling_verb_user_daily('POST', url) def test_throttling_put_ip_burst(self): @@ -943,7 +944,7 @@ def test_addon_blocked_guid_in_xpi(self): qs = Addon.unfiltered.filter(guid=guid) assert not qs.exists() filename = self.xpi_filepath('@create-webextension-with-guid', '1.0') - url = reverse_ns('signing.version') + url = reverse_ns('signing.version', api_version='v4') response = self.request( 'POST', guid=guid, version='1.0', filename=filename, url=url @@ -974,7 +975,7 @@ def test_deleted_webextension(self): guid=guid, version='1.0', filename=self.xpi_filepath('@create-webextension-with-guid', '1.0'), - url=reverse_ns('signing.version'), + url=reverse_ns('signing.version', api_version='v4'), ) assert response.status_code == 400 assert response.data['error'] == 'Duplicate add-on ID found.' @@ -984,7 +985,7 @@ class TestUploadVersionWebextension(BaseUploadVersionTestMixin, TestCase): def test_addon_does_not_exist_webextension(self): response = self.request( 'POST', - url=reverse_ns('signing.version'), + url=reverse_ns('signing.version', api_version='v4'), guid='@create-webextension', version='1.0', extra_kwargs={'REMOTE_ADDR': '127.0.3.1'}, @@ -1015,7 +1016,7 @@ def test_post_addon_restricted(self): EmailUserRestriction.objects.create(email_pattern=self.user.email) response = self.request( 'POST', - url=reverse_ns('signing.version'), + url=reverse_ns('signing.version', api_version='v4'), guid='@create-webextension', version='1.0', ) @@ -1028,7 +1029,7 @@ def test_post_addon_restricted(self): IPNetworkUserRestriction.objects.create(network='127.0.0.1/32') response = self.request( 'POST', - url=reverse_ns('signing.version'), + url=reverse_ns('signing.version', api_version='v4'), guid='@create-webextension', version='1.0', ) @@ -1061,7 +1062,7 @@ def test_post_addon_restricted_by_reputation_ip(self): ) response = self.request( 'POST', - url=reverse_ns('signing.version'), + url=reverse_ns('signing.version', api_version='v4'), guid='@create-webextension', version='1.0', ) @@ -1095,7 +1096,7 @@ def test_post_addon_restricted_by_reputation_email(self): ) response = self.request( 'POST', - url=reverse_ns('signing.version'), + url=reverse_ns('signing.version', api_version='v4'), guid='@create-webextension', version='1.0', ) @@ -1162,7 +1163,7 @@ def test_deleted_webextension(self): def test_webextension_reuse_guid(self): response = self.request( 'POST', - url=reverse_ns('signing.version'), + url=reverse_ns('signing.version', api_version='v4'), guid='@create-webextension-with-guid', version='1.0', ) @@ -1178,7 +1179,7 @@ def test_webextension_reuse_guid_but_only_create(self): # have to use the regular `PUT` endpoint for that. response = self.request( 'POST', - url=reverse_ns('signing.version'), + url=reverse_ns('signing.version', api_version='v4'), guid='@create-webextension-with-guid', version='1.0', ) @@ -1186,7 +1187,7 @@ def test_webextension_reuse_guid_but_only_create(self): response = self.request( 'POST', - url=reverse_ns('signing.version'), + url=reverse_ns('signing.version', api_version='v4'), guid='@create-webextension-with-guid', version='1.0', ) @@ -1198,7 +1199,7 @@ def test_webextension_optional_version(self): # have to use the regular `PUT` endpoint for that. response = self.request( 'POST', - url=reverse_ns('signing.version'), + url=reverse_ns('signing.version', api_version='v4'), guid='@create-webextension-with-guid-and-version', version='99.0', ) @@ -1211,7 +1212,7 @@ def test_webextension_resolve_translations(self): response = self.request( 'POST', - url=reverse_ns('signing.version'), + url=reverse_ns('signing.version', api_version='v4'), guid='@notify-link-clicks-i18n', version='1.0', filename=fname, @@ -1273,7 +1274,7 @@ class TestTestUploadVersionWebextensionTransactions( # ActivityLog/UserRestrictionHistory objects to be saved. def test_activity_log_saved_on_throttling(self): - url = reverse_ns('signing.version') + url = reverse_ns('signing.version', api_version='v4') with freeze_time('2019-04-08 15:16:23.42'): for x in range(0, 3): self._add_fake_throttling_action( @@ -1303,7 +1304,7 @@ def test_activity_log_saved_on_throttling(self): def test_user_restriction_history_saved_on_permission_denied(self): EmailUserRestriction.objects.create(email_pattern=self.user.email) - url = reverse_ns('signing.version') + url = reverse_ns('signing.version', api_version='v4') response = self.request( 'POST', url=url, @@ -1422,7 +1423,7 @@ def test_version_download_url(self): assert response.status_code == 200 file_ = qs.get() assert response.data['files'][0]['download_url'] == absolutify( - reverse_ns('signing.file', kwargs={'file_id': file_.id}) + reverse_ns('signing.file', api_version='v4', kwargs={'file_id': file_.id}) + f'/{file_.pretty_filename}' ) @@ -1491,7 +1492,7 @@ def setUp(self): self.file_ = self.create_file() def url(self): - return reverse_ns('signing.file', args=[self.file_.pk]) + return reverse_ns('signing.file', api_version='v4', args=[self.file_.pk]) def create_file(self): addon = addon_factory( diff --git a/src/olympia/signing/views.py b/src/olympia/signing/views.py index 8c12445b1201..93401b8c47fb 100644 --- a/src/olympia/signing/views.py +++ b/src/olympia/signing/views.py @@ -27,10 +27,10 @@ from olympia.devhub.permissions import IsSubmissionAllowedFor from olympia.files.models import FileUpload from olympia.files.utils import parse_addon -from olympia.signing.serializers import SigningFileUploadSerializer from olympia.versions import views as version_views from olympia.versions.models import Version +from .serializers import SigningFileUploadSerializer log = olympia.core.logger.getLogger('signing')