Beispiel #1
0
def check_xpi_info(xpi_info, addon=None, xpi_file=None):
    from olympia.addons.models import Addon, DeniedGuid
    guid = xpi_info['guid']
    is_webextension = xpi_info.get('is_webextension', False)

    # If we allow the guid to be omitted we assume that one was generated
    # or existed before and use that one.
    # An example are WebExtensions that don't require a guid but we generate
    # one once they're uploaded. Now, if you update that WebExtension we
    # just use the original guid.
    if addon and not guid and is_webextension:
        xpi_info['guid'] = guid = addon.guid
    if not guid and not is_webextension:
        raise forms.ValidationError(ugettext('Could not find an add-on ID.'))

    if guid:
        current_user = core.get_user()
        if current_user:
            deleted_guid_clashes = Addon.unfiltered.exclude(
                authors__id=current_user.id).filter(guid=guid)
        else:
            deleted_guid_clashes = Addon.unfiltered.filter(guid=guid)

        if addon and addon.guid != guid:
            msg = ugettext(
                'The add-on ID in your manifest.json or install.rdf (%s) '
                'does not match the ID of your add-on on AMO (%s)')
            raise forms.ValidationError(msg % (guid, addon.guid))
        if (not addon and
                # Non-deleted add-ons.
            (
                Addon.objects.filter(guid=guid).exists() or
                # DeniedGuid objects for legacy deletions.
                DeniedGuid.objects.filter(guid=guid).exists() or
                # Deleted add-ons that don't belong to the uploader.
                deleted_guid_clashes.exists())):
            raise forms.ValidationError(ugettext('Duplicate add-on ID found.'))
    if len(xpi_info['version']) > 32:
        raise forms.ValidationError(
            ugettext('Version numbers should have fewer than 32 characters.'))
    if not VERSION_RE.match(xpi_info['version']):
        raise forms.ValidationError(
            ugettext('Version numbers should only contain letters, numbers, '
                     'and these punctuation characters: +*.-_.'))

    if is_webextension and xpi_info.get('type') == amo.ADDON_STATICTHEME:
        if not waffle.switch_is_active('allow-static-theme-uploads'):
            raise forms.ValidationError(
                ugettext(
                    'WebExtension theme uploads are currently not supported.'))

    if xpi_file:
        # Make sure we pass in a copy of `xpi_info` since
        # `resolve_webext_translations` modifies data in-place
        translations = Addon.resolve_webext_translations(
            xpi_info.copy(), xpi_file)
        verify_mozilla_trademark(translations['name'], core.get_user())

    return xpi_info
    def test_set_task_user(self):
        @decorators.set_task_user
        def some_func():
            return core.get_user()

        core.set_user(UserProfile.objects.get(username='******'))
        assert core.get_user().pk == 999
        assert some_func().pk == int(settings.TASK_USER_ID)
        assert core.get_user().pk == 999
Beispiel #3
0
    def test_set_task_user(self):
        @decorators.set_task_user
        def some_func():
            return core.get_user()

        core.set_user(UserProfile.objects.get(username='******'))
        assert core.get_user().pk == 999
        assert some_func().pk == int(settings.TASK_USER_ID)
        assert core.get_user().pk == 999
Beispiel #4
0
def test_set_get_user_anonymous():
    core.set_user(AnonymousUser())
    assert core.get_user() is None

    user = UserProfile()
    core.set_user(user)
    assert core.get_user() == user

    core.set_user(None)
    assert core.get_user() is None
Beispiel #5
0
    def ban_and_disable_related_content_bulk(cls, users, move_files=False):
        """Admin method to ban users and disable the content they produced.

        Similar to deletion, except that the content produced by the user is
        forcibly disabled instead of being deleted where possible, and the user
        is not fully anonymized: we keep their fxa_id and email so that they
        are never able to log back in.
        """
        from olympia.addons.models import Addon, AddonUser
        from olympia.addons.tasks import index_addons
        from olympia.bandwagon.models import Collection
        from olympia.files.models import File
        from olympia.ratings.models import Rating

        # collect affected addons
        addon_ids = set(
            Addon.unfiltered.exclude(status=amo.STATUS_DELETED).filter(
                addonuser__user__in=users).values_list('id', flat=True))

        # First addons who have other authors we aren't banning
        addon_joint_ids = set(
            AddonUser.objects.filter(addon_id__in=addon_ids).exclude(
                user__in=users).values_list('addon_id', flat=True))
        AddonUser.objects.filter(user__in=users,
                                 addon_id__in=addon_joint_ids).delete()

        # Then deal with users who are the sole author
        addons_sole = Addon.unfiltered.filter(id__in=addon_ids -
                                              addon_joint_ids)
        # set the status to disabled - using the manager update() method
        addons_sole.update(status=amo.STATUS_DISABLED)
        # collect Files that need to be disabled now the addons are disabled
        files_to_disable = File.objects.filter(version__addon__in=addons_sole)
        files_to_disable.update(status=amo.STATUS_DISABLED)
        if move_files:
            # if necessary move the files out of the CDN (expensive operation)
            for file_ in files_to_disable:
                file_.hide_disabled_file()

        # Finally run Addon.force_disable to add the logging; update versions
        # Status was already DISABLED so shouldn't fire watch_disabled again.
        for addon in addons_sole:
            addon.force_disable()
        # Don't pass a set to a .delay - sets can't be serialized as JSON
        index_addons.delay(list(addon_ids - addon_joint_ids))

        # delete the other content associated with the user
        Collection.objects.filter(author__in=users).delete()
        Rating.objects.filter(user__in=users).delete(
            user_responsible=core.get_user())
        # And then delete the users.
        for user in users:
            log.info(f'User ({user}: <{user.email}>) is being '
                     'anonymized and banned.')
            user.banned = user.modified = datetime.now()
            user.deleted = True
        cls.anonymize_users(users)
        cls.objects.bulk_update(users,
                                fields=('banned', 'deleted', 'modified') +
                                cls.ANONYMIZED_FIELDS)
Beispiel #6
0
def new_theme_version_with_69_properties(old_version):
    timer = StopWatch(
        'addons.tasks.repack_themes_for_69.new_theme_version.')
    timer.start()

    author = get_user()
    # Wrap zip in FileUpload for Version from_upload to consume.
    upload = FileUpload.objects.create(user=author, valid=True)
    filename = uuid.uuid4().hex + '.xpi'
    destination = os.path.join(user_media_path('addons'), 'temp', filename)
    old_xpi = get_filepath(old_version.all_files[0])
    build_69_compatible_theme(
        old_xpi, destination, get_next_version_number(old_version.addon))
    upload.update(path=destination, name=filename)
    timer.log_interval('1.build_xpi')

    # Create addon + version
    parsed_data = parse_addon(upload, addon=old_version.addon, user=author)
    timer.log_interval('2.parse_addon')

    version = Version.from_upload(
        upload, old_version.addon, selected_apps=[amo.FIREFOX.id],
        channel=amo.RELEASE_CHANNEL_LISTED,
        parsed_data=parsed_data)
    timer.log_interval('3.initialize_version')

    # And finally sign the files (actually just one)
    for file_ in version.all_files:
        sign_file(file_)
        file_.update(
            reviewed=datetime.now(),
            status=amo.STATUS_APPROVED)
    timer.log_interval('4.sign_files')

    return version
Beispiel #7
0
def extract_theme_properties(addon, channel):
    version = addon.find_latest_version(channel)
    if not version or not version.all_files:
        return {}
    try:
        parsed_data = parse_xpi(
            version.all_files[0].file_path, addon=addon, user=core.get_user())
    except ValidationError:
        # If we can't parse the existing manifest safely return.
        return {}
    theme_props = parsed_data.get('theme', {})
    # pre-process colors to convert chrome style colors and strip spaces
    theme_props['colors'] = dict(
        process_color_value(prop, color)
        for prop, color in theme_props.get('colors', {}).items())
    # replace headerURL with path to existing background
    if 'images' in theme_props:
        if 'theme_frame' in theme_props['images']:
            header_url = theme_props['images'].pop('theme_frame')
        if 'headerURL' in theme_props['images']:
            header_url = theme_props['images'].pop('headerURL')
        if header_url:
            theme_props['images']['headerURL'] = '/'.join((
                user_media_url('addons'), text_type(addon.id),
                text_type(version.id), header_url))
    return theme_props
Beispiel #8
0
    def create(cls, action, *args, **kw):
        """
        e.g. ActivityLog.create(amo.LOG.CREATE_ADDON, addon),
             ActivityLog.create(amo.LOG.ADD_FILE_TO_VERSION, file, version)
        In case of circular import you can use `olympia.activity.log_create()`
        """
        from olympia import core

        user = kw.get('user', core.get_user())

        if not user:
            log.warning('Activity log called with no user: %s' % action.id)
            return

        # We make sure that we take the timestamp if provided, instead of
        # creating a new one, especially useful for log entries created
        # in a loop.
        al = ActivityLog(
            user=user, action=action.id,
            created=kw.get('created', timezone.now()))
        al.arguments = args
        if 'details' in kw:
            al.details = kw['details']
        al.save()

        if 'details' in kw and 'comments' in al.details:
            CommentLog.objects.create(
                comments=al.details['comments'], activity_log=al,
                created=kw.get('created', timezone.now()))

        for arg in args:
            if isinstance(arg, tuple):
                class_ = arg[0]
                id_ = arg[1]
            else:
                class_ = arg.__class__
                id_ = arg.id if isinstance(arg, ModelBase) else None

            if class_ == Addon:
                AddonLog.objects.create(
                    addon_id=id_, activity_log=al,
                    created=kw.get('created', timezone.now()))
            elif class_ == Version:
                VersionLog.objects.create(
                    version_id=id_, activity_log=al,
                    created=kw.get('created', timezone.now()))
            elif class_ == UserProfile:
                UserLog.objects.create(
                    user_id=id_, activity_log=al,
                    created=kw.get('created', timezone.now()))
            elif class_ == Group:
                GroupLog.objects.create(
                    group_id=id_, activity_log=al,
                    created=kw.get('created', timezone.now()))

        # Index by every user
        UserLog.objects.create(
            activity_log=al, user=user,
            created=kw.get('created', timezone.now()))
        return al
Beispiel #9
0
def new_theme_version_with_69_properties(old_version):
    timer = StopWatch(
        'addons.tasks.repack_themes_for_69.new_theme_version.')
    timer.start()

    author = get_user()
    # Wrap zip in FileUpload for Version from_upload to consume.
    upload = FileUpload.objects.create(user=author, valid=True)
    filename = uuid.uuid4().hex + '.xpi'
    destination = os.path.join(user_media_path('addons'), 'temp', filename)
    old_xpi = get_filepath(old_version.all_files[0])
    build_69_compatible_theme(
        old_xpi, destination, get_next_version_number(old_version.addon))
    upload.update(path=destination, name=filename)
    timer.log_interval('1.build_xpi')

    # Create addon + version
    parsed_data = parse_addon(upload, addon=old_version.addon, user=author)
    timer.log_interval('2.parse_addon')

    version = Version.from_upload(
        upload, old_version.addon, selected_apps=[amo.FIREFOX.id],
        channel=amo.RELEASE_CHANNEL_LISTED,
        parsed_data=parsed_data)
    timer.log_interval('3.initialize_version')

    # And finally sign the files (actually just one)
    for file_ in version.all_files:
        sign_file(file_)
        file_.update(
            reviewed=datetime.now(),
            status=amo.STATUS_APPROVED)
    timer.log_interval('4.sign_files')

    return version
Beispiel #10
0
 def wrapper(*args, **kw):
     old_user = core.get_user()
     core.set_user(get_task_user())
     try:
         result = f(*args, **kw)
     finally:
         core.set_user(old_user)
     return result
Beispiel #11
0
 def process(self, msg, kwargs):
     kwargs.setdefault('extra', {}).update({
         'REMOTE_ADDR':
         core.get_remote_addr() or '',
         'USERNAME':
         getattr(core.get_user(), 'username', None) or '<anon>',
     })
     return msg, kwargs
Beispiel #12
0
 def wrapper(*args, **kw):
     old_user = core.get_user()
     core.set_user(get_task_user())
     try:
         result = f(*args, **kw)
     finally:
         core.set_user(old_user)
     return result
Beispiel #13
0
def check_xpi_info(xpi_info, addon=None):
    from olympia.addons.models import Addon, DeniedGuid
    guid = xpi_info['guid']
    is_webextension = xpi_info.get('is_webextension', False)

    # If we allow the guid to be omitted we assume that one was generated
    # or existed before and use that one.
    # An example are WebExtensions that don't require a guid but we generate
    # one once they're uploaded. Now, if you update that WebExtension we
    # just use the original guid.
    if addon and not guid and is_webextension:
        xpi_info['guid'] = guid = addon.guid
    if not guid and not is_webextension:
        raise forms.ValidationError(ugettext('Could not find an add-on ID.'))

    if guid:
        current_user = core.get_user()
        if current_user:
            deleted_guid_clashes = Addon.unfiltered.exclude(
                authors__id=current_user.id).filter(guid=guid)
        else:
            deleted_guid_clashes = Addon.unfiltered.filter(guid=guid)
        guid_too_long = (
            not waffle.switch_is_active('allow-long-addon-guid') and
            len(guid) > 64
        )
        if guid_too_long:
            raise forms.ValidationError(
                ugettext('Add-on ID must be 64 characters or less.'))
        if addon and addon.guid != guid:
            msg = ugettext(
                'The add-on ID in your manifest.json or install.rdf (%s) '
                'does not match the ID of your add-on on AMO (%s)')
            raise forms.ValidationError(msg % (guid, addon.guid))
        if (not addon and
            # Non-deleted add-ons.
            (Addon.objects.filter(guid=guid).exists() or
             # DeniedGuid objects for legacy deletions.
             DeniedGuid.objects.filter(guid=guid).exists() or
             # Deleted add-ons that don't belong to the uploader.
             deleted_guid_clashes.exists())):
            raise forms.ValidationError(ugettext('Duplicate add-on ID found.'))
    if len(xpi_info['version']) > 32:
        raise forms.ValidationError(
            ugettext('Version numbers should have fewer than 32 characters.'))
    if not VERSION_RE.match(xpi_info['version']):
        raise forms.ValidationError(
            ugettext('Version numbers should only contain letters, numbers, '
                     'and these punctuation characters: +*.-_.'))

    if is_webextension and xpi_info.get('is_static_theme', False):
        if not waffle.switch_is_active('allow-static-theme-uploads'):
            raise forms.ValidationError(ugettext(
                'WebExtension theme uploads are currently not supported.'))

    return xpi_info
Beispiel #14
0
 def _delete_related_content(self, *, addon_msg=''):
     """Delete content produced by this user if they are the only author."""
     self.collections.all().delete()
     for addon in self.addons.all().iterator():
         if not addon.authors.exclude(pk=self.pk).exists():
             addon.delete(msg=addon_msg)
         else:
             addon.addonuser_set.get(user=self).delete()
     user_responsible = core.get_user()
     self._ratings_all.all().delete(user_responsible=user_responsible)
Beispiel #15
0
def check_xpi_info(xpi_info, addon=None):
    from olympia.addons.models import Addon, DeniedGuid
    guid = xpi_info['guid']
    is_webextension = xpi_info.get('is_webextension', False)

    # If we allow the guid to be omitted we assume that one was generated
    # or existed before and use that one.
    # An example are WebExtensions that don't require a guid but we generate
    # one once they're uploaded. Now, if you update that WebExtension we
    # just use the original guid.
    if addon and not guid and is_webextension:
        xpi_info['guid'] = guid = addon.guid
    if not guid and not is_webextension:
        raise forms.ValidationError(_("Could not find an add-on ID."))

    if guid:
        current_user = core.get_user()
        if current_user:
            deleted_guid_clashes = Addon.unfiltered.exclude(
                authors__id=current_user.id).filter(guid=guid)
        else:
            deleted_guid_clashes = Addon.unfiltered.filter(guid=guid)
        guid_too_long = (not waffle.switch_is_active('allow-long-addon-guid')
                         and len(guid) > 64)
        if guid_too_long:
            raise forms.ValidationError(
                _("Add-on ID must be 64 characters or less."))
        if addon and addon.guid != guid:
            msg = _("The add-on ID in your manifest.json or install.rdf (%s) "
                    "does not match the ID of your add-on on AMO (%s)")
            raise forms.ValidationError(msg % (guid, addon.guid))
        if (not addon and
                # Non-deleted add-ons.
            (
                Addon.objects.filter(guid=guid).exists() or
                # DeniedGuid objects for legacy deletions.
                DeniedGuid.objects.filter(guid=guid).exists() or
                # Deleted add-ons that don't belong to the uploader.
                deleted_guid_clashes.exists())):
            raise forms.ValidationError(_('Duplicate add-on ID found.'))
    if len(xpi_info['version']) > 32:
        raise forms.ValidationError(
            _('Version numbers should have fewer than 32 characters.'))
    if not VERSION_RE.match(xpi_info['version']):
        raise forms.ValidationError(
            _('Version numbers should only contain letters, numbers, '
              'and these punctuation characters: +*.-_.'))

    if is_webextension and xpi_info.get('is_static_theme', False):
        if not waffle.switch_is_active('allow-static-theme-uploads'):
            raise forms.ValidationError(
                _('WebExtension theme uploads are currently not supported.'))

    return xpi_info
Beispiel #16
0
 def delete_or_disable_related_content(self, delete=False):
     """Delete or disable content produced by this user if they are the only
     author."""
     self.collections.all().delete()
     for addon in self.addons.all().iterator():
         if not addon.authors.exclude(pk=self.pk).exists():
             if delete:
                 addon.delete()
             else:
                 addon.force_disable()
     user_responsible = core.get_user()
     self._ratings_all.all().delete(user_responsible=user_responsible)
     self.delete_picture()
Beispiel #17
0
def start_validation(request):
    # FIXME: `@transaction.non_atomic_requests` is a workaround for an issue
    # that might exist elsewhere too. The view is wrapped in a transaction
    # by default and because of that tasks being started in this view
    # won't see the `ValidationJob` object created.
    form = BulkValidationForm(request.POST)
    if form.is_valid():
        job = form.save(commit=False)
        job.creator = core.get_user()
        job.save()
        find_files(job)
        return redirect(reverse('zadmin.validation'))
    else:
        return validation(request, form=form)
Beispiel #18
0
 def delete_or_disable_related_content(self, delete=False):
     """Delete or disable content produced by this user if they are the only
     author."""
     self.collections.all().delete()
     for addon in self.addons.all().iterator():
         if not addon.authors.exclude(pk=self.pk).exists():
             if delete:
                 addon.delete()
             else:
                 addon.force_disable()
         else:
             addon.addonuser_set.filter(user=self).delete()
     user_responsible = core.get_user()
     self._ratings_all.all().delete(user_responsible=user_responsible)
     self.delete_picture()
Beispiel #19
0
def extract_theme_properties(addon, channel):
    version = addon.find_latest_version(channel)
    if not version or not version.all_files:
        return {}
    try:
        parsed_data = parse_xpi(
            version.all_files[0].file_path, addon=addon, user=core.get_user())
    except ValidationError:
        # If we can't parse the existing manifest safely return.
        return {}
    theme_props = parsed_data.get('theme', {})
    # pre-process colors to convert chrome style colors and strip spaces
    theme_props['colors'] = dict(
        process_color_value(prop, color)
        for prop, color in theme_props.get('colors', {}).items())
    return theme_props
def extract_theme_properties(addon, channel):
    version = addon.find_latest_version(channel)
    if not version or not version.all_files:
        return {}
    try:
        parsed_data = parse_xpi(
            version.all_files[0].file_path, addon=addon, user=core.get_user())
    except ValidationError:
        # If we can't parse the existing manifest safely return.
        return {}
    theme_props = parsed_data.get('theme', {})
    # pre-process colors to convert chrome style colors and strip spaces
    theme_props['colors'] = dict(
        process_color_value(prop, color)
        for prop, color in theme_props.get('colors', {}).items())
    return theme_props
Beispiel #21
0
    def ban_and_disable_related_content_bulk(cls, users, move_files=False):
        """Like ban_and_disable_related_content, but in bulk. """
        from olympia.addons.models import Addon, AddonUser
        from olympia.addons.tasks import index_addons
        from olympia.bandwagon.models import Collection
        from olympia.files.models import File
        from olympia.ratings.models import Rating

        # collect affected addons
        addon_ids = set(
            Addon.unfiltered.exclude(status=amo.STATUS_DELETED).filter(
                addonuser__user__in=users).values_list('id', flat=True))

        # First addons who have other authors we aren't banning
        addon_joint_ids = set(
            AddonUser.objects.filter(addon_id__in=addon_ids).exclude(
                user__in=users).values_list('addon_id', flat=True))
        AddonUser.objects.filter(user__in=users,
                                 addon_id__in=addon_joint_ids).delete()

        # Then deal with users who are the sole author
        addons_sole = Addon.unfiltered.filter(id__in=addon_ids -
                                              addon_joint_ids)
        # set the status to disabled - using the manager update() method
        addons_sole.update(status=amo.STATUS_DISABLED)
        # collect Files that need to be disabled now the addons are disabled
        files_to_disable = File.objects.filter(version__addon__in=addons_sole)
        files_to_disable.update(status=amo.STATUS_DISABLED)
        if move_files:
            # if necessary move the files out of the CDN (expensive operation)
            for file_ in files_to_disable:
                file_.hide_disabled_file()

        # Finally run Addon.force_disable to add the logging; update versions
        # Status was already DISABLED so shouldn't fire watch_disabled again.
        for addon in addons_sole:
            addon.force_disable()
        # Don't pass a set to a .delay - sets can't be serialized as JSON
        index_addons.delay(list(addon_ids - addon_joint_ids))

        # delete the other content associated with the user
        Collection.objects.filter(author__in=users).delete()
        Rating.objects.filter(user__in=users).delete(
            user_responsible=core.get_user())
        # And then delete the users.
        for user in users:
            user.delete(ban_user=True)
Beispiel #22
0
    def ban_and_disable_related_content_bulk(cls, users, move_files=False):
        """Like ban_and_disable_related_content, but in bulk. """
        from olympia.addons.models import Addon, AddonUser
        from olympia.addons.tasks import index_addons
        from olympia.bandwagon.models import Collection
        from olympia.files.models import File
        from olympia.ratings.models import Rating

        # collect affected addons
        addon_ids = set(
            Addon.unfiltered.exclude(status=amo.STATUS_DELETED)
            .filter(addonuser__user__in=users).values_list('id', flat=True))

        # First addons who have other authors we aren't banning
        addon_joint_ids = set(
            AddonUser.objects.filter(addon_id__in=addon_ids)
            .exclude(user__in=users).values_list('addon_id', flat=True))
        AddonUser.objects.filter(
            user__in=users, addon_id__in=addon_joint_ids).delete()

        # Then deal with users who are the sole author
        addons_sole = Addon.unfiltered.filter(
            id__in=addon_ids - addon_joint_ids)
        # set the status to disabled - using the manager update() method
        addons_sole.update(status=amo.STATUS_DISABLED)
        # collect Files that need to be disabled now the addons are disabled
        files_to_disable = File.objects.filter(version__addon__in=addons_sole)
        files_to_disable.update(status=amo.STATUS_DISABLED)
        if move_files:
            # if necessary move the files out of the CDN (expensive operation)
            for file_ in files_to_disable:
                file_.hide_disabled_file()

        # Finally run Addon.force_disable to add the logging; update versions
        # Status was already DISABLED so shouldn't fire watch_disabled again.
        for addon in addons_sole:
            addon.force_disable()
        index_addons.delay(addon_ids - addon_joint_ids)

        # delete the other content associated with the user
        Collection.objects.filter(author__in=users).delete()
        Rating.objects.filter(user__in=users).delete(
            user_responsible=core.get_user())
        # And then delete the users.
        for user in users:
            user.delete(keep_fxa_id_and_email=True)
Beispiel #23
0
    def create(cls, action, *args, **kw):
        """
        e.g. ActivityLog.create(amo.LOG.CREATE_ADDON, addon),
             ActivityLog.create(amo.LOG.ADD_FILE_TO_VERSION, file, version)
        In case of circular import you can use `olympia.activity.log_create()`
        """
        from olympia import core

        user = kw.get('user', core.get_user())

        if not user:
            log.warning('Activity log called with no user: %s' % action.id)
            return

        al = ActivityLog(user=user, action=action.id)
        al.arguments = args
        if 'details' in kw:
            al.details = kw['details']
        al.save()

        if 'details' in kw and 'comments' in al.details:
            CommentLog(comments=al.details['comments'], activity_log=al).save()

        for arg in args:
            if isinstance(arg, tuple):
                if arg[0] == Addon:
                    AddonLog(addon_id=arg[1], activity_log=al).save()
                elif arg[0] == Version:
                    VersionLog(version_id=arg[1], activity_log=al).save()
                elif arg[0] == UserProfile:
                    UserLog(user_id=arg[1], activity_log=al).save()
                elif arg[0] == Group:
                    GroupLog(group_id=arg[1], activity_log=al).save()
            elif isinstance(arg, Addon):
                AddonLog(addon=arg, activity_log=al).save()
            elif isinstance(arg, Version):
                VersionLog(version=arg, activity_log=al).save()
            elif isinstance(arg, UserProfile):
                # Index by any user who is mentioned as an argument.
                UserLog(activity_log=al, user=arg).save()
            elif isinstance(arg, Group):
                GroupLog(group=arg, activity_log=al).save()

        # Index by every user
        UserLog(activity_log=al, user=user).save()
        return al
Beispiel #24
0
    def create(cls, action, *args, **kw):
        """
        e.g. ActivityLog.create(amo.LOG.CREATE_ADDON, addon),
             ActivityLog.create(amo.LOG.ADD_FILE_TO_VERSION, file, version)
        In case of circular import you can use `olympia.activity.log_create()`
        """
        from olympia import core

        user = kw.get('user', core.get_user())

        if not user:
            log.warning('Activity log called with no user: %s' % action.id)
            return

        al = ActivityLog(user=user, action=action.id)
        al.arguments = args
        if 'details' in kw:
            al.details = kw['details']
        al.save()

        if 'details' in kw and 'comments' in al.details:
            CommentLog(comments=al.details['comments'], activity_log=al).save()

        for arg in args:
            if isinstance(arg, tuple):
                if arg[0] == Addon:
                    AddonLog(addon_id=arg[1], activity_log=al).save()
                elif arg[0] == Version:
                    VersionLog(version_id=arg[1], activity_log=al).save()
                elif arg[0] == UserProfile:
                    UserLog(user_id=arg[1], activity_log=al).save()
                elif arg[0] == Group:
                    GroupLog(group_id=arg[1], activity_log=al).save()
            elif isinstance(arg, Addon):
                AddonLog(addon=arg, activity_log=al).save()
            elif isinstance(arg, Version):
                VersionLog(version=arg, activity_log=al).save()
            elif isinstance(arg, UserProfile):
                # Index by any user who is mentioned as an argument.
                UserLog(activity_log=al, user=arg).save()
            elif isinstance(arg, Group):
                GroupLog(group=arg, activity_log=al).save()

        # Index by every user
        UserLog(activity_log=al, user=user).save()
        return al
Beispiel #25
0
def extract_theme_properties(addon, channel):
    version = addon.find_latest_version(channel)
    if not version or not version.all_files:
        return {}
    try:
        parsed_data = parse_xpi(
            version.all_files[0].file_path, addon=addon, user=core.get_user())
    except ValidationError:
        # If we can't parse the existing manifest safely return.
        return {}
    theme_props = parsed_data.get('theme', {})
    # pre-process colors to deprecated colors; strip spaces.
    theme_props['colors'] = dict(
        process_color_value(prop, color)
        for prop, color in theme_props.get('colors', {}).items())
    # upgrade manifest from deprecated headerURL to theme_frame
    if 'headerURL' in theme_props.get('images', {}):
        url = theme_props['images'].pop('headerURL')
        theme_props['images']['theme_frame'] = url
    return theme_props
Beispiel #26
0
def check_xpi_info(xpi_info, addon=None, xpi_file=None, user=None):
    from olympia.addons.models import Addon, DeniedGuid
    guid = xpi_info['guid']
    is_webextension = xpi_info.get('is_webextension', False)

    # If we allow the guid to be omitted we assume that one was generated
    # or existed before and use that one.
    # An example are WebExtensions that don't require a guid but we generate
    # one once they're uploaded. Now, if you update that WebExtension we
    # just use the original guid.
    if addon and not guid and is_webextension:
        xpi_info['guid'] = guid = addon.guid
    if not guid and not is_webextension:
        raise forms.ValidationError(ugettext('Could not find an add-on ID.'))

    if guid:
        current_user = core.get_user()
        if current_user:
            deleted_guid_clashes = Addon.unfiltered.exclude(
                authors__id=current_user.id).filter(guid=guid)
        else:
            deleted_guid_clashes = Addon.unfiltered.filter(guid=guid)

        if addon and addon.guid != guid:
            msg = ugettext(
                'The add-on ID in your manifest.json or install.rdf (%s) '
                'does not match the ID of your add-on on AMO (%s)')
            raise forms.ValidationError(msg % (guid, addon.guid))
        if (not addon and
            # Non-deleted add-ons.
            (Addon.objects.filter(guid=guid).exists() or
             # DeniedGuid objects for deletions for Mozilla disabled add-ons
             DeniedGuid.objects.filter(guid=guid).exists() or
             # Deleted add-ons that don't belong to the uploader.
             deleted_guid_clashes.exists())):
            raise forms.ValidationError(ugettext('Duplicate add-on ID found.'))
    if len(xpi_info['version']) > 32:
        raise forms.ValidationError(
            ugettext('Version numbers should have fewer than 32 characters.'))
    if not VERSION_RE.match(xpi_info['version']):
        raise forms.ValidationError(
            ugettext('Version numbers should only contain letters, numbers, '
                     'and these punctuation characters: +*.-_.'))

    if is_webextension and xpi_info.get('type') == amo.ADDON_STATICTHEME:
        if not waffle.switch_is_active('allow-static-theme-uploads'):
            raise forms.ValidationError(ugettext(
                'WebExtension theme uploads are currently not supported.'))

    if xpi_file:
        # Make sure we pass in a copy of `xpi_info` since
        # `resolve_webext_translations` modifies data in-place
        translations = Addon.resolve_webext_translations(
            xpi_info.copy(), xpi_file)
        verify_mozilla_trademark(translations['name'], core.get_user())

    # Parse the file to get and validate package data with the addon.
    if not acl.submission_allowed(user, xpi_info):
        raise forms.ValidationError(
            ugettext(u'You cannot submit this type of add-on'))

    if not addon and not system_addon_submission_allowed(
            user, xpi_info):
        guids = ' or '.join(
                '"' + guid + '"' for guid in amo.SYSTEM_ADDON_GUIDS)
        raise forms.ValidationError(
            ugettext(u'You cannot submit an add-on with a guid ending '
                     u'%s' % guids))

    if not mozilla_signed_extension_submission_allowed(user, xpi_info):
        raise forms.ValidationError(
            ugettext(u'You cannot submit a Mozilla Signed Extension'))

    return xpi_info
Beispiel #27
0
    def create(cls, action, *args, **kw):
        """
        e.g. ActivityLog.create(amo.LOG.CREATE_ADDON, addon),
             ActivityLog.create(amo.LOG.ADD_FILE_TO_VERSION, file, version)
        In case of circular import you can use `olympia.activity.log_create()`
        """
        from olympia import core

        user = kw.get('user', core.get_user())

        if not user:
            log.warning('Activity log called with no user: %s' % action.id)
            return

        al = ActivityLog(user=user, action=action.id)
        al.arguments = args
        if 'details' in kw:
            al.details = kw['details']
        al.save()

        # We make sure that we take the timestamp if provided, instead of
        # creating a new one, especially useful for log entries created
        # in a loop.
        if 'created' in kw:
            al.update(created=kw.get('created'))

        if 'details' in kw and 'comments' in al.details:
            cl = CommentLog(comments=al.details['comments'], activity_log=al)
            cl.save()
            if 'created' in kw:
                cl.update(created=kw.get('created'))

        for arg in args:
            if isinstance(arg, tuple):
                if arg[0] == Addon:
                    addon_l = AddonLog(addon_id=arg[1], activity_log=al)
                    addon_l.save()
                    if 'created' in kw:
                        addon_l.update(created=kw.get('created'))
                elif arg[0] == Version:
                    vl = VersionLog(version_id=arg[1], activity_log=al)
                    vl.save()
                    if 'created' in kw:
                        vl.update(created=kw.get('created'))
                elif arg[0] == UserProfile:
                    ul = UserLog(user_id=arg[1], activity_log=al)
                    ul.save()
                    if 'created' in kw:
                        ul.update(created=kw.get('created'))
                elif arg[0] == Group:
                    gl = GroupLog(group_id=arg[1], activity_log=al)
                    gl.save()
                    if 'created' in kw:
                        gl.update(created=kw.get('created'))
            elif isinstance(arg, Addon):
                addon_l = AddonLog(addon=arg, activity_log=al)
                addon_l.save()
                if 'created' in kw:
                    addon_l.update(created=kw.get('created'))
            elif isinstance(arg, Version):
                vl = VersionLog(version=arg, activity_log=al)
                vl.save()
                if 'created' in kw:
                    vl.update(created=kw.get('created'))
            elif isinstance(arg, UserProfile):
                # Index by any user who is mentioned as an argument.
                ul = UserLog(activity_log=al, user=arg)
                ul.save()
                if 'created' in kw:
                    ul.update(created=kw.get('created'))
            elif isinstance(arg, Group):
                gl = GroupLog(group=arg, activity_log=al)
                gl.save()
                if 'created' in kw:
                    gl.update(created=kw.get('created'))

        # Index by every user
        ul = UserLog(activity_log=al, user=user)
        ul.save()
        if 'created' in kw:
            ul.update(created=kw.get('created'))
        return al
 def some_func():
     return core.get_user()
Beispiel #29
0
    def create(cls, action, *args, **kw):
        """
        e.g. ActivityLog.create(amo.LOG.CREATE_ADDON, addon),
             ActivityLog.create(amo.LOG.ADD_FILE_TO_VERSION, file, version)
        In case of circular import you can use `olympia.activity.log_create()`
        """
        from olympia import core

        user = kw.get('user', core.get_user())

        if not user:
            log.warning('Activity log called with no user: %s' % action.id)
            return

        al = ActivityLog(user=user, action=action.id)
        al.arguments = args
        if 'details' in kw:
            al.details = kw['details']
        al.save()

        # We make sure that we take the timestamp if provided, instead of
        # creating a new one, especially useful for log entries created
        # in a loop.
        if 'created' in kw:
            al.update(created=kw.get('created'))

        if 'details' in kw and 'comments' in al.details:
            cl = CommentLog(comments=al.details['comments'], activity_log=al)
            cl.save()
            if 'created' in kw:
                cl.update(created=kw.get('created'))

        for arg in args:
            if isinstance(arg, tuple):
                if arg[0] == Addon:
                    addon_l = AddonLog(addon_id=arg[1], activity_log=al)
                    addon_l.save()
                    if 'created' in kw:
                        addon_l.update(created=kw.get('created'))
                elif arg[0] == Version:
                    vl = VersionLog(version_id=arg[1], activity_log=al)
                    vl.save()
                    if 'created' in kw:
                        vl.update(created=kw.get('created'))
                elif arg[0] == UserProfile:
                    ul = UserLog(user_id=arg[1], activity_log=al)
                    ul.save()
                    if 'created' in kw:
                        ul.update(created=kw.get('created'))
                elif arg[0] == Group:
                    gl = GroupLog(group_id=arg[1], activity_log=al)
                    gl.save()
                    if 'created' in kw:
                        gl.update(created=kw.get('created'))
            elif isinstance(arg, Addon):
                addon_l = AddonLog(addon=arg, activity_log=al)
                addon_l.save()
                if 'created' in kw:
                    addon_l.update(created=kw.get('created'))
            elif isinstance(arg, Version):
                vl = VersionLog(version=arg, activity_log=al)
                vl.save()
                if 'created' in kw:
                    vl.update(created=kw.get('created'))
            elif isinstance(arg, UserProfile):
                # Index by any user who is mentioned as an argument.
                ul = UserLog(activity_log=al, user=arg)
                ul.save()
                if 'created' in kw:
                    ul.update(created=kw.get('created'))
            elif isinstance(arg, Group):
                gl = GroupLog(group=arg, activity_log=al)
                gl.save()
                if 'created' in kw:
                    gl.update(created=kw.get('created'))

        # Index by every user
        ul = UserLog(activity_log=al, user=user)
        ul.save()
        if 'created' in kw:
            ul.update(created=kw.get('created'))
        return al
Beispiel #30
0
    def create(cls, action, *args, **kw):
        """
        e.g. ActivityLog.create(amo.LOG.CREATE_ADDON, addon),
             ActivityLog.create(amo.LOG.ADD_FILE_TO_VERSION, file, version)
        In case of circular import you can use `olympia.activity.log_create()`
        """
        from olympia import core

        user = kw.get('user', core.get_user())

        if not user:
            log.warning('Activity log called with no user: %s' % action.id)
            return

        # We make sure that we take the timestamp if provided, instead of
        # creating a new one, especially useful for log entries created
        # in a loop.
        al = ActivityLog(user=user,
                         action=action.id,
                         created=kw.get('created', timezone.now()))
        al.set_arguments(args)
        if 'details' in kw:
            al.details = kw['details']
        al.save()

        if 'details' in kw and 'comments' in al.details:
            CommentLog.objects.create(
                comments=al.details['comments'],
                activity_log=al,
                created=kw.get('created', timezone.now()),
            )

        for arg in args:
            create_kwargs = {
                'activity_log': al,
                'created': kw.get('created', timezone.now()),
            }
            if isinstance(arg, tuple):
                class_ = arg[0]
                id_ = arg[1]
            else:
                class_ = arg.__class__
                id_ = arg.id if isinstance(arg, ModelBase) else None

            if class_ == Addon:
                AddonLog.objects.create(addon_id=id_, **create_kwargs)
            elif class_ == Version:
                VersionLog.objects.create(version_id=id_, **create_kwargs)
            elif class_ == UserProfile:
                UserLog.objects.create(user_id=id_, **create_kwargs)
            elif class_ == Group:
                GroupLog.objects.create(group_id=id_, **create_kwargs)
            elif class_ == Block:
                BlockLog.objects.create(block_id=id_,
                                        guid=arg.guid,
                                        **create_kwargs)
            elif class_ == ReviewActionReason:
                ReviewActionReasonLog.objects.create(reason_id=id_,
                                                     **create_kwargs)

        if getattr(action, 'store_ip', False):
            # Index specific actions by their IP address. Note that the caller
            # must take care of overriding remote addr if the action is created
            # from a task.
            IPLog.objects.create(
                ip_address=core.get_remote_addr(),
                activity_log=al,
                created=kw.get('created', timezone.now()),
            )

        # Index by every user
        UserLog.objects.create(activity_log=al,
                               user=user,
                               created=kw.get('created', timezone.now()))
        return al
 def process(self, msg, kwargs):
     kwargs['extra'] = {
         'REMOTE_ADDR': core.get_remote_addr() or '',
         'USERNAME': getattr(core.get_user(), 'username', None) or '<anon>'
     }
     return msg, kwargs
Beispiel #32
0
def check_xpi_info(xpi_info, addon=None, xpi_file=None, user=None):
    from olympia.addons.models import Addon, DeniedGuid
    guid = xpi_info['guid']
    is_webextension = xpi_info.get('is_webextension', False)

    # If we allow the guid to be omitted we assume that one was generated
    # or existed before and use that one.
    # An example are WebExtensions that don't require a guid but we generate
    # one once they're uploaded. Now, if you update that WebExtension we
    # just use the original guid.
    if addon and not guid and is_webextension:
        xpi_info['guid'] = guid = addon.guid
    if not guid and not is_webextension:
        raise forms.ValidationError(ugettext('Could not find an add-on ID.'))

    if guid:
        current_user = core.get_user()
        if current_user:
            deleted_guid_clashes = Addon.unfiltered.exclude(
                authors__id=current_user.id).filter(guid=guid)
        else:
            deleted_guid_clashes = Addon.unfiltered.filter(guid=guid)

        if addon and addon.guid != guid:
            msg = ugettext(
                'The add-on ID in your manifest.json or install.rdf (%s) '
                'does not match the ID of your add-on on AMO (%s)')
            raise forms.ValidationError(msg % (guid, addon.guid))
        if (not addon and
                # Non-deleted add-ons.
            (
                Addon.objects.filter(guid=guid).exists() or
                # DeniedGuid objects for deletions for Mozilla disabled add-ons
                DeniedGuid.objects.filter(guid=guid).exists() or
                # Deleted add-ons that don't belong to the uploader.
                deleted_guid_clashes.exists())):
            raise forms.ValidationError(ugettext('Duplicate add-on ID found.'))
    if len(xpi_info['version']) > 32:
        raise forms.ValidationError(
            ugettext('Version numbers should have fewer than 32 characters.'))
    if not VERSION_RE.match(xpi_info['version']):
        raise forms.ValidationError(
            ugettext('Version numbers should only contain letters, numbers, '
                     'and these punctuation characters: +*.-_.'))

    if is_webextension and xpi_info.get('type') == amo.ADDON_STATICTHEME:
        max_size = settings.MAX_STATICTHEME_SIZE
        if xpi_file and os.path.getsize(xpi_file.name) > max_size:
            raise forms.ValidationError(
                ugettext(
                    u'Maximum size for WebExtension themes is {0}.').format(
                        filesizeformat(max_size)))

    if xpi_file:
        # Make sure we pass in a copy of `xpi_info` since
        # `resolve_webext_translations` modifies data in-place
        translations = Addon.resolve_webext_translations(
            xpi_info.copy(), xpi_file)
        verify_mozilla_trademark(translations['name'], core.get_user())

    # Parse the file to get and validate package data with the addon.
    if not acl.submission_allowed(user, xpi_info):
        raise forms.ValidationError(
            ugettext(u'You cannot submit this type of add-on'))

    if not addon and not system_addon_submission_allowed(user, xpi_info):
        guids = ' or '.join('"' + guid + '"'
                            for guid in amo.SYSTEM_ADDON_GUIDS)
        raise forms.ValidationError(
            ugettext(u'You cannot submit an add-on with a guid ending '
                     u'%s' % guids))

    if not mozilla_signed_extension_submission_allowed(user, xpi_info):
        raise forms.ValidationError(
            ugettext(u'You cannot submit a Mozilla Signed Extension'))

    return xpi_info
Beispiel #33
0
 def process(self, msg, kwargs):
     kwargs['extra'] = {
         'REMOTE_ADDR': core.get_remote_addr() or '',
         'USERNAME': getattr(core.get_user(), 'username', None) or '<anon>'
     }
     return msg, kwargs
Beispiel #34
0
 def some_func():
     return core.get_user()