示例#1
0
 def test_is_submission_allowed_bypassing_read_dev_agreement_restricted(
         self, incr_mock):
     # Mix of test_is_submission_allowed_email_restricted() and
     # test_is_submission_allowed_bypassing_read_dev_agreement() above:
     # this time, we're restricted by email while bypassing the read dev
     # agreement check. This ensures even when bypassing that check, we
     # still record everything properly when restricting.
     EmailUserRestriction.objects.create(
         email_pattern=self.request.user.email)
     checker = RestrictionChecker(request=self.request)
     assert not checker.is_submission_allowed(check_dev_agreement=False)
     assert checker.get_error_message() == (
         'The email address used for your account is not '
         'allowed for add-on submission.')
     assert incr_mock.call_count == 2
     assert incr_mock.call_args_list[0][0] == (
         'RestrictionChecker.is_submission_allowed.EmailUserRestriction.failure',
     )
     assert incr_mock.call_args_list[1][0] == (
         'RestrictionChecker.is_submission_allowed.failure', )
     assert UserRestrictionHistory.objects.count() == 1
     history = UserRestrictionHistory.objects.get()
     assert history.get_restriction_display() == 'EmailUserRestriction'
     assert history.user == self.request.user
     assert history.last_login_ip == self.request.user.last_login_ip
     assert history.ip_address == '10.0.0.1'
示例#2
0
 def has_permission(self, request, view):
     checker = RestrictionChecker(request=request)
     if not checker.is_submission_allowed():
         self.message = checker.get_error_message()
         self.code = 'permission_denied_restriction'
         return False
     return True
示例#3
0
 def test_is_auto_approval_allowed_email_restricted(self, incr_mock):
     EmailUserRestriction.objects.create(
         email_pattern=self.request.user.email,
         restriction_type=RESTRICTION_TYPES.APPROVAL,
     )
     upload = FileUpload.objects.create(
         user=self.request.user,
         ip_address='10.0.0.2',
         source=amo.UPLOAD_SOURCE_DEVHUB,
     )
     incr_mock.reset_mock()
     checker = RestrictionChecker(upload=upload)
     assert not checker.is_auto_approval_allowed()
     assert incr_mock.call_count == 2
     assert incr_mock.call_args_list[0][0] == (
         'RestrictionChecker.is_auto_approval_allowed.EmailUserRestriction.failure',
     )
     assert incr_mock.call_args_list[1][0] == (
         'RestrictionChecker.is_auto_approval_allowed.failure', )
     assert UserRestrictionHistory.objects.count() == 1
     history = UserRestrictionHistory.objects.get()
     assert history.get_restriction_display() == 'EmailUserRestriction'
     assert history.user == self.request.user
     assert history.last_login_ip == self.request.user.last_login_ip
     assert history.ip_address == '10.0.0.2'
示例#4
0
 def test_is_submission_allowed_pass(self, incr_mock):
     checker = RestrictionChecker(request=self.request)
     assert checker.is_submission_allowed()
     assert incr_mock.call_count == 1
     assert incr_mock.call_args_list[0][0] == (
         'RestrictionChecker.is_submission_allowed.success', )
     assert not UserRestrictionHistory.objects.exists()
示例#5
0
 def test_user_is_allowed_to_bypass_restrictions(self, incr_mock):
     IPNetworkUserRestriction.objects.create(network='10.0.0.0/24')
     EmailUserRestriction.objects.create(email_pattern=self.request.user.email)
     self.request.user.update(bypass_upload_restrictions=True)
     checker = RestrictionChecker(request=self.request)
     assert checker.is_submission_allowed()
     assert not UserRestrictionHistory.objects.exists()
     assert incr_mock.call_count == 0
示例#6
0
 def test_is_submission_allowed_bypassing_read_dev_agreement(
         self, incr_mock):
     self.request.user.update(read_dev_agreement=None)
     checker = RestrictionChecker(request=self.request)
     assert checker.is_submission_allowed(check_dev_agreement=False)
     assert incr_mock.call_count == 1
     assert incr_mock.call_args_list[0][0] == (
         'RestrictionChecker.is_submission_allowed.success', )
     assert not UserRestrictionHistory.objects.exists()
示例#7
0
 def test_is_auto_approval_allowed_email_restricted_only_for_submission(
         self, incr_mock):
     # Test with a submission restriction (the default): approval should be allowed.
     EmailUserRestriction.objects.create(
         email_pattern=self.request.user.email)
     upload = FileUpload.objects.create(user=self.request.user,
                                        ip_address='10.0.0.2')
     incr_mock.reset_mock()
     checker = RestrictionChecker(upload=upload)
     assert checker.is_auto_approval_allowed()
     assert incr_mock.call_count == 1
     assert UserRestrictionHistory.objects.count() == 0
     assert incr_mock.call_args_list[0][0] == (
         'RestrictionChecker.is_auto_approval_allowed.success', )
示例#8
0
 def test_is_submission_allowed_with_mocks(self, incr_mock):
     checker = RestrictionChecker(request=self.request)
     with ExitStack() as stack:
         allow_submission_mocks = [
             stack.enter_context(mock.patch.object(choice[1], 'allow_submission'))
             for choice in checker.restriction_choices
         ]
         allow_auto_approval_mocks = [
             stack.enter_context(mock.patch.object(choice[1], 'allow_auto_approval'))
             for choice in checker.restriction_choices
         ]
         assert checker.is_submission_allowed()
     for restriction_mock in allow_submission_mocks:
         assert restriction_mock.call_count == 1
     for restriction_mock in allow_auto_approval_mocks:
         assert restriction_mock.call_count == 0
示例#9
0
 def test_is_submission_allowed_ip_restricted(self, incr_mock):
     IPNetworkUserRestriction.objects.create(network='10.0.0.0/24')
     checker = RestrictionChecker(request=self.request)
     assert not checker.is_submission_allowed()
     assert checker.get_error_message() == (
         'Multiple add-ons violating our policies have been submitted '
         'from your location. The IP address has been blocked.')
     assert incr_mock.call_count == 2
     assert incr_mock.call_args_list[0][0] == (
         'RestrictionChecker.is_submission_allowed.IPNetworkUserRestriction.failure',
     )
     assert incr_mock.call_args_list[1][0] == (
         'RestrictionChecker.is_submission_allowed.failure', )
     assert UserRestrictionHistory.objects.count() == 1
     history = UserRestrictionHistory.objects.get()
     assert history.get_restriction_display() == 'IPNetworkUserRestriction'
     assert history.user == self.request.user
     assert history.last_login_ip == self.request.user.last_login_ip
     assert history.ip_address == '10.0.0.1'
示例#10
0
 def test_is_submission_allowed_email_restricted(self, incr_mock):
     EmailUserRestriction.objects.create(
         email_pattern=self.request.user.email)
     checker = RestrictionChecker(request=self.request)
     assert not checker.is_submission_allowed()
     assert checker.get_error_message() == (
         'The email address used for your account is not '
         'allowed for add-on submission.')
     assert incr_mock.call_count == 2
     assert incr_mock.call_args_list[0][0] == (
         'RestrictionChecker.is_submission_allowed.EmailUserRestriction.failure',
     )
     assert incr_mock.call_args_list[1][0] == (
         'RestrictionChecker.is_submission_allowed.failure', )
     assert UserRestrictionHistory.objects.count() == 1
     history = UserRestrictionHistory.objects.get()
     assert history.get_restriction_display() == 'EmailUserRestriction'
     assert history.user == self.request.user
     assert history.last_login_ip == self.request.user.last_login_ip
     assert history.ip_address == '10.0.0.1'
示例#11
0
 def test_is_auto_approval_allowed_with_mocks(self, incr_mock):
     upload = FileUpload.objects.create(user=self.request.user,
                                        ip_address='10.0.0.2')
     checker = RestrictionChecker(upload=upload)
     with ExitStack() as stack:
         allow_submission_mocks = [
             stack.enter_context(
                 mock.patch.object(choice[1], 'allow_submission'))
             for choice in checker.restriction_choices
         ]
         allow_auto_approval_mocks = [
             stack.enter_context(
                 mock.patch.object(choice[1], 'allow_auto_approval'))
             for choice in checker.restriction_choices
         ]
         assert checker.is_auto_approval_allowed()
     for restriction_mock in allow_submission_mocks:
         assert restriction_mock.call_count == 0
     for restriction_mock in allow_auto_approval_mocks:
         assert restriction_mock.call_count == 1
示例#12
0
 def test_is_submission_allowed_hasnt_read_agreement(self, incr_mock):
     self.request.user.update(read_dev_agreement=None)
     checker = RestrictionChecker(request=self.request)
     assert not checker.is_submission_allowed()
     assert checker.get_error_message() == (
         'Before starting, please read and accept our Firefox Add-on '
         'Distribution Agreement as well as our Review Policies and Rules. '
         'The Firefox Add-on Distribution Agreement also links to our '
         'Privacy Notice which explains how we handle your information.')
     assert incr_mock.call_count == 2
     assert incr_mock.call_args_list[0][0] == (
         'RestrictionChecker.is_submission_allowed.DeveloperAgreementRestriction.'
         'failure', )
     assert incr_mock.call_args_list[1][0] == (
         'RestrictionChecker.is_submission_allowed.failure', )
     assert UserRestrictionHistory.objects.count() == 1
     history = UserRestrictionHistory.objects.get()
     assert history.get_restriction_display() == (
         'DeveloperAgreementRestriction')
     assert history.user == self.request.user
     assert history.last_login_ip == self.request.user.last_login_ip
     assert history.ip_address == '10.0.0.1'
示例#13
0
    def from_upload(cls,
                    upload,
                    addon,
                    selected_apps,
                    channel,
                    parsed_data=None):
        """
        Create a Version instance and corresponding File(s) from a
        FileUpload, an Addon, a list of compatible app ids, a channel id and
        the parsed_data generated by parse_addon().

        Note that it's the caller's responsability to ensure the file is valid.
        We can't check for that here because an admin may have overridden the
        validation results.
        """
        from olympia.addons.models import AddonReviewerFlags
        from olympia.addons.utils import RestrictionChecker
        from olympia.git.utils import create_git_extraction_entry

        assert parsed_data is not None

        if addon.status == amo.STATUS_DISABLED:
            raise VersionCreateError(
                'Addon is Mozilla Disabled; no new versions are allowed.')

        if upload.addon and upload.addon != addon:
            raise VersionCreateError(
                'FileUpload was made for a different Addon')

        if not upload.user or not upload.ip_address or not upload.source:
            raise VersionCreateError(
                'FileUpload does not have some required fields')

        if not upload.user.last_login_ip or not upload.user.email:
            raise VersionCreateError(
                'FileUpload user does not have some required fields')

        license_id = None
        if channel == amo.RELEASE_CHANNEL_LISTED:
            previous_version = addon.find_latest_version(channel=channel,
                                                         exclude=())
            if previous_version and previous_version.license_id:
                license_id = previous_version.license_id
        approval_notes = None
        if parsed_data.get('is_mozilla_signed_extension'):
            approval_notes = (
                'This version has been signed with Mozilla internal certificate.'
            )
        version = cls.objects.create(
            addon=addon,
            approval_notes=approval_notes,
            version=parsed_data['version'],
            license_id=license_id,
            channel=channel,
        )
        email = upload.user.email if upload.user and upload.user.email else ''
        with core.override_remote_addr(upload.ip_address):
            # The following log statement is used by foxsec-pipeline.
            # We override the IP because it might be called from a task and we
            # want the original IP from the submitter.
            log.info(
                f'New version: {version!r} ({version.id}) from {upload!r}',
                extra={
                    'email': email,
                    'guid': addon.guid,
                    'upload': upload.uuid.hex,
                    'user_id': upload.user_id,
                    'from_api': upload.source == amo.UPLOAD_SOURCE_API,
                },
            )
            activity.log_create(amo.LOG.ADD_VERSION,
                                version,
                                addon,
                                user=upload.user or get_task_user())

        if addon.type == amo.ADDON_STATICTHEME:
            # We don't let developers select apps for static themes
            selected_apps = [app.id for app in amo.APP_USAGE]

        compatible_apps = {}
        for app in parsed_data.get('apps', []):
            if app.id not in selected_apps:
                # If the user chose to explicitly deselect Firefox for Android
                # we're not creating the respective `ApplicationsVersions`
                # which will have this add-on then be listed only for
                # Firefox specifically.
                continue

            compatible_apps[app.appdata] = ApplicationsVersions(
                version=version, min=app.min, max=app.max, application=app.id)
            compatible_apps[app.appdata].save()

        # Pre-generate _compatible_apps property to avoid accidentally
        # triggering queries with that instance later.
        version._compatible_apps = compatible_apps

        # Create relevant file and update the all_files cached property on the
        # Version, because we might need it afterwards.
        version.all_files = [
            File.from_upload(
                upload=upload,
                version=version,
                parsed_data=parsed_data,
            )
        ]

        version.inherit_nomination(from_statuses=[amo.STATUS_AWAITING_REVIEW])
        version.disable_old_files()

        # After the upload has been copied to its permanent location, delete it
        # from storage. Keep the FileUpload instance (it gets cleaned up by a
        # cron eventually some time after its creation, in amo.cron.gc()),
        # making sure it's associated with the add-on instance.
        storage.delete(upload.path)
        upload.path = ''
        if upload.addon is None:
            upload.addon = addon
        upload.save()

        version_uploaded.send(instance=version, sender=Version)

        if version.is_webextension:
            if (waffle.switch_is_active('enable-yara')
                    or waffle.switch_is_active('enable-customs')
                    or waffle.switch_is_active('enable-wat')):
                ScannerResult.objects.filter(upload_id=upload.id).update(
                    version=version)

        if waffle.switch_is_active('enable-uploads-commit-to-git-storage'):
            # Schedule this version for git extraction.
            transaction.on_commit(
                lambda: create_git_extraction_entry(version=version))

        # Generate a preview and icon for listed static themes
        if (addon.type == amo.ADDON_STATICTHEME
                and channel == amo.RELEASE_CHANNEL_LISTED):
            theme_data = parsed_data.get('theme', {})
            generate_static_theme_preview(theme_data, version.pk)

        # Reset add-on reviewer flags to disable auto-approval and require
        # admin code review if the package has already been signed by mozilla.
        reviewer_flags_defaults = {}
        is_mozilla_signed = parsed_data.get('is_mozilla_signed_extension')
        if upload.validation_timeout:
            reviewer_flags_defaults['needs_admin_code_review'] = True
        if is_mozilla_signed and addon.type != amo.ADDON_LPAPP:
            reviewer_flags_defaults['needs_admin_code_review'] = True
            reviewer_flags_defaults['auto_approval_disabled'] = True

        # Check if the approval should be restricted
        if not RestrictionChecker(upload=upload).is_auto_approval_allowed():
            flag = ('auto_approval_disabled'
                    if channel == amo.RELEASE_CHANNEL_LISTED else
                    'auto_approval_disabled_unlisted')
            reviewer_flags_defaults[flag] = True

        if reviewer_flags_defaults:
            AddonReviewerFlags.objects.update_or_create(
                addon=addon, defaults=reviewer_flags_defaults)

        # Authors need to be notified about auto-approval delay again since
        # they are submitting a new version.
        addon.reset_notified_about_auto_approval_delay()

        # Track the time it took from first upload through validation
        # (and whatever else) until a version was created.
        upload_start = utc_millesecs_from_epoch(upload.created)
        now = datetime.datetime.now()
        now_ts = utc_millesecs_from_epoch(now)
        upload_time = now_ts - upload_start

        log.info('Time for version {version} creation from upload: {delta}; '
                 'created={created}; now={now}'.format(delta=upload_time,
                                                       version=version,
                                                       created=upload.created,
                                                       now=now))
        statsd.timing('devhub.version_created_from_upload', upload_time)

        return version
示例#14
0
 def test_is_auto_approval_allowed_no_upload_raises_improperly_configured(
         self, incr_mock):
     checker = RestrictionChecker(request=self.request)
     with self.assertRaises(ImproperlyConfigured):
         assert checker.is_auto_approval_allowed()
示例#15
0
 def test_is_submission_allowed_no_request_raises_improperly_configured(
         self, incr_mock):
     checker = RestrictionChecker(upload=mock.Mock())
     with self.assertRaises(ImproperlyConfigured):
         assert checker.is_submission_allowed()
示例#16
0
 def test_no_request_or_upload_at_init(self, incr_mock):
     with self.assertRaises(ImproperlyConfigured):
         RestrictionChecker()