Exemplo n.º 1
0
        def wrapper(request, addon, *args, **kw):
            from olympia.devhub.views import _resume
            if theme:
                kw['theme'] = addon.is_persona()
            elif addon.is_persona():
                # Don't allow theme views if theme not passed in.
                raise http.Http404

            def fun():
                return f(request, addon_id=addon.id, addon=addon, *args, **kw)

            if allow_editors:
                if acl.is_editor(request, addon):
                    return fun()
            # Require an owner or dev for POST requests.
            if request.method == 'POST':
                if acl.check_addon_ownership(request,
                                             addon,
                                             dev=not owner_for_post):
                    return fun()
            # Ignore disabled so they can view their add-on.
            elif acl.check_addon_ownership(request,
                                           addon,
                                           viewer=True,
                                           ignore_disabled=True):
                step = SubmitStep.objects.filter(addon=addon)
                # Redirect to the submit flow if they're not done.
                if not getattr(f, 'submitting', False) and step:
                    return _resume(addon, step)
                return fun()
            raise PermissionDenied
Exemplo n.º 2
0
        def wrapper(request, addon, *args, **kw):
            def fun():
                return f(request, addon_id=addon.id, addon=addon, *args, **kw)

            if request.method in ('HEAD', 'GET'):
                # Allow reviewers for read operations.
                if allow_reviewers_for_read and (acl.is_reviewer(
                        request, addon) or acl.action_allowed(
                            request, permissions.REVIEWER_TOOLS_VIEW)):
                    return fun()
                # On read-only requests, ignore disabled so developers can
                # still view their add-on.
                if acl.check_addon_ownership(request,
                                             addon,
                                             dev=not owner_for_get,
                                             ignore_disabled=True):
                    # Redirect to the submit flow if they're not done.
                    if (not submitting
                            and addon.should_redirect_to_submit_flow()):
                        return redirect('devhub.submit.details', addon.slug)
                    return fun()
            # Require an owner or dev for POST requests (if the add-on status
            # is disabled that check will return False).
            elif request.method == 'POST':
                if acl.check_addon_ownership(request,
                                             addon,
                                             dev=not owner_for_post):
                    return fun()
            raise PermissionDenied
Exemplo n.º 3
0
        def wrapper(request, addon, *args, **kw):
            if theme:
                kw['theme'] = addon.is_persona()
            elif addon.is_persona():
                # Don't allow theme views if theme not passed in.
                raise http.Http404

            def fun():
                return f(request, addon_id=addon.id, addon=addon, *args, **kw)

            if allow_editors:
                if acl.is_editor(request, addon):
                    return fun()
            # Require an owner or dev for POST requests.
            if request.method == 'POST':
                if acl.check_addon_ownership(request, addon,
                                             dev=not owner_for_post):
                    return fun()
            # Ignore disabled so they can view their add-on.
            elif acl.check_addon_ownership(request, addon, viewer=True,
                                           ignore_disabled=True):
                # Redirect to the submit flow if they're not done.
                if (not submitting and addon.status == amo.STATUS_NULL and
                        not addon.has_complete_metadata()):
                    return redirect('devhub.submit.details', addon.slug)
                return fun()
            raise PermissionDenied
Exemplo n.º 4
0
        def wrapper(request, addon, *args, **kw):
            if theme:
                kw['theme'] = addon.is_persona()
            elif addon.is_persona():
                # Don't allow theme views if theme not passed in.
                raise http.Http404

            def fun():
                return f(request, addon_id=addon.id, addon=addon, *args, **kw)

            if allow_editors:
                if acl.is_editor(request, addon):
                    return fun()
            # Require an owner or dev for POST requests.
            if request.method == 'POST':
                if acl.check_addon_ownership(request, addon,
                                             dev=not owner_for_post):
                    return fun()
            # Ignore disabled so they can view their add-on.
            elif acl.check_addon_ownership(request, addon, viewer=True,
                                           ignore_disabled=True):
                # Redirect to the submit flow if they're not done.
                if (not submitting and addon.should_redirect_to_submit_flow()):
                    return redirect('devhub.submit.details', addon.slug)
                return fun()
            raise PermissionDenied
Exemplo n.º 5
0
        def wrapper(request, addon, *args, **kw):
            from olympia.devhub.views import _resume
            if theme:
                kw['theme'] = addon.is_persona()
            elif addon.is_persona():
                # Don't allow theme views if theme not passed in.
                raise http.Http404

            def fun():
                return f(request, addon_id=addon.id, addon=addon, *args, **kw)

            if allow_editors:
                if acl.is_editor(request, addon):
                    return fun()
            # Require an owner or dev for POST requests.
            if request.method == 'POST':
                if acl.check_addon_ownership(request, addon,
                                             dev=not owner_for_post):
                    return fun()
            # Ignore disabled so they can view their add-on.
            elif acl.check_addon_ownership(request, addon, viewer=True,
                                           ignore_disabled=True):
                step = SubmitStep.objects.filter(addon=addon)
                # Redirect to the submit flow if they're not done.
                if not getattr(f, 'submitting', False) and step:
                    return _resume(addon, step)
                return fun()
            raise PermissionDenied
Exemplo n.º 6
0
        def wrapper(request, addon, *args, **kw):
            def fun():
                return f(request, addon_id=addon.id, addon=addon, *args, **kw)

            if submitting and addon.type == amo.ADDON_SITE_PERMISSION:
                raise PermissionDenied
            if request.method in ('HEAD', 'GET'):
                # Allow reviewers for read operations, if file_id is present
                # and the reviewer is the right kind of reviewer for this file.
                if allow_reviewers_for_read:
                    file_id = kw.get('file_id')
                    if file_id:
                        is_unlisted = Version.unfiltered.filter(
                            file__id=file_id, channel=amo.RELEASE_CHANNEL_UNLISTED
                        ).exists()
                        has_required_permission = (
                            acl.check_unlisted_addons_viewer_or_reviewer(request)
                            if is_unlisted
                            else (acl.check_listed_addons_viewer_or_reviewer(request))
                        )
                        if has_required_permission:
                            return fun()
                    else:
                        raise ImproperlyConfigured

                # On read-only requests, we can allow developers, and even let
                # authors see mozilla disabled or site permission add-ons.
                if acl.check_addon_ownership(
                    request,
                    addon,
                    allow_developer=True,
                    allow_mozilla_disabled_addon=True,
                    allow_site_permission=True,
                ):
                    # Redirect to the submit flow if they're not done with
                    # listed submission.
                    if not submitting and addon.should_redirect_to_submit_flow():
                        return redirect('devhub.submit.details', addon.slug)
                    return fun()
            # Require an owner or deveveloper for POST requests (if the add-on
            # status is disabled that check will return False).
            elif request.method == 'POST':
                if acl.check_addon_ownership(
                    request,
                    addon,
                    allow_developer=not owner_for_post,
                    allow_site_permission=allow_site_permission_for_post,
                ):
                    return fun()
            raise PermissionDenied
Exemplo n.º 7
0
def download_file(request, file_id, type=None, file_=None, addon=None):
    def is_appropriate_reviewer(addon, channel):
        return (acl.is_reviewer(request, addon)
                if channel == amo.RELEASE_CHANNEL_LISTED
                else acl.check_unlisted_addons_reviewer(request))

    if not file_:
        file_ = get_object_or_404(File.objects, pk=file_id)
    if not addon:
        addon = get_object_or_404(Addon.objects,
                                  pk=file_.version.addon_id)
    channel = file_.version.channel

    if addon.is_disabled or file_.status == amo.STATUS_DISABLED:
        if (is_appropriate_reviewer(addon, channel) or
                acl.check_addon_ownership(
                    request, addon, dev=True, ignore_disabled=True)):
            return HttpResponseSendFile(
                request, file_.guarded_file_path,
                content_type='application/x-xpinstall')
        else:
            log.info(
                u'download file {file_id}: addon/file disabled and '
                u'user {user_id} is not an owner or reviewer.'.format(
                    file_id=file_id, user_id=request.user.pk))
            raise http.Http404()  # Not owner or admin.

    if channel == amo.RELEASE_CHANNEL_UNLISTED:
        if (acl.check_unlisted_addons_reviewer(request) or
                acl.check_addon_ownership(
                    request, addon, dev=True, ignore_disabled=True)):
            return HttpResponseSendFile(
                request, file_.file_path,
                content_type='application/x-xpinstall')
        else:
            log.info(
                u'download file {file_id}: version is unlisted and '
                u'user {user_id} is not an owner or reviewer.'.format(
                    file_id=file_id, user_id=request.user.pk))
            raise http.Http404()  # Not owner or admin.

    attachment = bool(type == 'attachment')

    loc = urlparams(file_.get_file_cdn_url(attachment=attachment),
                    filehash=file_.hash)
    response = http.HttpResponseRedirect(loc)
    response['X-Target-Digest'] = file_.hash
    return response
Exemplo n.º 8
0
def download_file(request, file_id, type=None, file_=None, addon=None):
    def is_appropriate_reviewer(addon, channel):
        return (acl.is_reviewer(request, addon)
                if channel == amo.RELEASE_CHANNEL_LISTED
                else acl.check_unlisted_addons_reviewer(request))

    if not file_:
        file_ = get_object_or_404(File.objects, pk=file_id)
    if not addon:
        addon = get_object_or_404(Addon.objects,
                                  pk=file_.version.addon_id)
    channel = file_.version.channel

    if addon.is_disabled or file_.status == amo.STATUS_DISABLED:
        if (is_appropriate_reviewer(addon, channel) or
                acl.check_addon_ownership(
                    request, addon, dev=True, ignore_disabled=True)):
            return HttpResponseSendFile(
                request, file_.guarded_file_path,
                content_type='application/x-xpinstall')
        else:
            log.info(
                u'download file {file_id}: addon/file disabled and '
                u'user {user_id} is not an owner or reviewer.'.format(
                    file_id=file_id, user_id=request.user.pk))
            raise http.Http404()  # Not owner or admin.

    if channel == amo.RELEASE_CHANNEL_UNLISTED:
        if (acl.check_unlisted_addons_reviewer(request) or
                acl.check_addon_ownership(
                    request, addon, dev=True, ignore_disabled=True)):
            return HttpResponseSendFile(
                request, file_.file_path,
                content_type='application/x-xpinstall')
        else:
            log.info(
                u'download file {file_id}: version is unlisted and '
                u'user {user_id} is not an owner or reviewer.'.format(
                    file_id=file_id, user_id=request.user.pk))
            raise http.Http404()  # Not owner or admin.

    attachment = (type == 'attachment' or not request.APP.browser)

    loc = urlparams(file_.get_file_cdn_url(attachment=attachment),
                    filehash=file_.hash)
    response = http.HttpResponseRedirect(loc)
    response['X-Target-Digest'] = file_.hash
    return response
Exemplo n.º 9
0
def download_file(request, file_id, type=None, file_=None, addon=None):
    """
    Download given file.

    `addon` and `file_` parameters can be passed to avoid the database query.

    If the file is disabled or belongs to an unlisted version, requires an
    add-on developer or appropriate reviewer for the channel. If the file is
    deleted or belongs to a deleted version or add-on, reviewers can still
    access but developers can't.
    """
    def is_appropriate_reviewer(addon, channel):
        return (acl.is_reviewer(request, addon)
                if channel == amo.RELEASE_CHANNEL_LISTED else
                acl.check_unlisted_addons_reviewer(request))

    if not file_:
        file_ = get_object_or_404(File.objects, pk=file_id)
    if not addon:
        # Include deleted add-ons in the queryset, we'll check for that below.
        addon = get_object_or_404(Addon.unfiltered, pk=file_.version.addon_id)
    version = file_.version
    channel = version.channel

    if version.deleted or addon.is_deleted:
        # Only the appropriate reviewer can see deleted things.
        use_cdn = False
        has_permission = is_appropriate_reviewer(addon, channel)
    elif (addon.is_disabled or file_.status == amo.STATUS_DISABLED
          or channel == amo.RELEASE_CHANNEL_UNLISTED):
        # Only the appropriate reviewer or developers of the add-on can see
        # disabled or unlisted things.
        use_cdn = False
        has_permission = (is_appropriate_reviewer(addon, channel)
                          or acl.check_addon_ownership(
                              request, addon, dev=True, ignore_disabled=True))
    else:
        # Everyone can see public things, and we can use the CDN in that case.
        use_cdn = True
        has_permission = True

    if not has_permission:
        log.info('download file {file_id}: addon/version/file not public and '
                 'user {user_id} does not have relevant permissions.'.format(
                     file_id=file_id, user_id=request.user.pk))
        raise http.Http404()  # Not owner or admin.

    if use_cdn:
        attachment = bool(type == 'attachment')
        loc = urlparams(file_.get_file_cdn_url(attachment=attachment),
                        filehash=file_.hash)
        response = http.HttpResponseRedirect(loc)
        response['X-Target-Digest'] = file_.hash
    else:
        response = HttpResponseXSendFile(
            request,
            file_.current_file_path,
            content_type='application/x-xpinstall')
    response['Access-Control-Allow-Origin'] = '*'
    return response
Exemplo n.º 10
0
def setup_viewer(request, file_obj):
    addon = file_obj.version.addon
    data = {
        'file': file_obj,
        'version': file_obj.version,
        'addon': addon,
        'status': False,
        'selected': {},
        'validate_url': ''
    }
    is_user_a_reviewer = acl.is_reviewer(request, addon)

    if (is_user_a_reviewer or acl.check_addon_ownership(
            request, addon, dev=True, ignore_disabled=True)):

        data['validate_url'] = reverse('devhub.json_file_validation',
                                       args=[addon.slug, file_obj.id])
        data['automated_signing'] = file_obj.automated_signing

        if file_obj.has_been_validated:
            data['validation_data'] = file_obj.validation.processed_validation

    if is_user_a_reviewer:
        data['file_link'] = {
            'text': ugettext('Back to review'),
            'url': reverse('reviewers.review', args=[addon.slug])
        }
    else:
        data['file_link'] = {
            'text': ugettext('Back to add-on'),
            'url': reverse('addons.detail', args=[addon.pk])
        }
    return data
Exemplo n.º 11
0
def setup_viewer(request, file_obj):
    data = {
        'file': file_obj,
        'version': file_obj.version,
        'addon': file_obj.version.addon,
        'status': False,
        'selected': {},
        'validate_url': ''
    }

    if (acl.check_addons_reviewer(request) or acl.check_addon_ownership(
            request, file_obj.version.addon, viewer=True,
            ignore_disabled=True)):

        addon = file_obj.version.addon

        data['validate_url'] = reverse('devhub.json_file_validation',
                                       args=[addon.slug, file_obj.id])
        data['automated_signing'] = file_obj.automated_signing

        if file_obj.has_been_validated:
            data['validation_data'] = file_obj.validation.processed_validation

    if acl.check_addons_reviewer(request):
        data['file_link'] = {
            'text': ugettext('Back to review'),
            'url': reverse('reviewers.review', args=[data['addon'].slug])
        }
    else:
        data['file_link'] = {
            'text': ugettext('Back to add-on'),
            'url': reverse('addons.detail', args=[data['addon'].pk])
        }
    return data
Exemplo n.º 12
0
def download_file(request, file_id, type=None, file_=None, addon=None):
    if not file_:
        file_ = get_object_or_404(File.objects, pk=file_id)
    if not addon:
        addon = get_object_or_404(Addon.with_unlisted, pk=file_.version.addon_id)

    if addon.is_disabled or file_.status == amo.STATUS_DISABLED:
        if acl.check_addon_ownership(request, addon, viewer=True, ignore_disabled=True) or acl.check_addons_reviewer(
            request
        ):
            return HttpResponseSendFile(request, file_.guarded_file_path, content_type="application/x-xpinstall")
        log.info(
            u"download file {file_id}: addon/file disabled or user "
            u"{user_id} is not an owner".format(file_id=file_id, user_id=request.user.pk)
        )
        raise http.Http404()

    if not (addon.is_listed or owner_or_unlisted_reviewer(request, addon)):
        log.info(
            u"download file {file_id}: addon is unlisted but user "
            u"{user_id} is not an owner".format(file_id=file_id, user_id=request.user.pk)
        )
        raise http.Http404  # Not listed, not owner or admin.

    attachment = type == "attachment" or not request.APP.browser

    loc = urlparams(file_.get_mirror(addon, attachment=attachment), filehash=file_.hash)
    response = http.HttpResponseRedirect(loc)
    response["X-Target-Digest"] = file_.hash
    return response
Exemplo n.º 13
0
def owner_or_unlisted_reviewer(request, addon):
    return (
        acl.check_unlisted_addons_reviewer(request) or
        # We don't want "admins" here, because it includes anyone with the
        # "Addons:Edit" perm, we only want those with
        # "Addons:ReviewUnlisted" perm (which is checked above).
        acl.check_addon_ownership(request, addon, admin=False, dev=True))
Exemplo n.º 14
0
def download_file(request, file_id, type=None, file_=None, addon=None):
    if not file_:
        file_ = get_object_or_404(File.objects, pk=file_id)
    if not addon:
        addon = get_object_or_404(Addon.with_unlisted,
                                  pk=file_.version.addon_id)

    if addon.is_disabled or file_.status == amo.STATUS_DISABLED:
        if (acl.check_addon_ownership(
                request, addon, viewer=True, ignore_disabled=True)
                or acl.check_addons_reviewer(request)):
            return HttpResponseSendFile(request,
                                        file_.guarded_file_path,
                                        content_type='application/x-xpinstall')
        log.info(u'download file {file_id}: addon/file disabled or user '
                 u'{user_id} is not an owner'.format(file_id=file_id,
                                                     user_id=request.user.pk))
        raise http.Http404()

    if not (addon.is_listed or owner_or_unlisted_reviewer(request, addon)):
        log.info(u'download file {file_id}: addon is unlisted but user '
                 u'{user_id} is not an owner'.format(file_id=file_id,
                                                     user_id=request.user.pk))
        raise http.Http404  # Not listed, not owner or admin.

    attachment = (type == 'attachment' or not request.APP.browser)

    loc = urlparams(file_.get_mirror(addon, attachment=attachment),
                    filehash=file_.hash)
    response = http.HttpResponseRedirect(loc)
    response['X-Target-Digest'] = file_.hash
    return response
Exemplo n.º 15
0
def owner_or_unlisted_reviewer(request, addon):
    return (acl.check_unlisted_addons_reviewer(request) or
            # We don't want "admins" here, because it includes anyone with the
            # "Addons:Edit" perm, we only want those with
            # "Addons:ReviewUnlisted" perm (which is checked above).
            acl.check_addon_ownership(request, addon, admin=False, dev=True,
                                      viewer=True, support=True))
Exemplo n.º 16
0
def reply(request, addon, review_id):
    is_admin = acl.action_allowed(request, 'Addons', 'Edit')
    is_author = acl.check_addon_ownership(request, addon, dev=True)
    if not (is_admin or is_author):
        raise PermissionDenied

    review = get_object_or_404(Review.objects, pk=review_id, addon=addon)
    form = forms.ReviewReplyForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        d = dict(reply_to=review, addon=addon,
                 defaults=dict(user=request.user))
        reply, new = Review.objects.get_or_create(**d)
        for key, val in _review_details(request, addon, form).items():
            setattr(reply, key, val)
        reply.save()
        action = 'New' if new else 'Edited'
        log.debug('%s reply to %s: %s' % (action, review_id, reply.id))

        if new:
            reply_url = helpers.url('addons.reviews.detail', addon.slug,
                                    review.id, add_prefix=False)
            data = {'name': addon.name,
                    'reply_title': reply.title,
                    'reply': reply.body,
                    'reply_url': helpers.absolutify(reply_url)}
            emails = [review.user.email]
            sub = u'Mozilla Add-on Developer Reply: %s' % addon.name
            send_mail('reviews/emails/reply_review.ltxt',
                      sub, emails, Context(data), 'reply')

        return redirect(helpers.url('addons.reviews.detail', addon.slug,
                                    review_id))
    ctx = dict(review=review, form=form, addon=addon)
    return render(request, 'reviews/reply.html', ctx)
Exemplo n.º 17
0
        def wrapper(request, addon, *args, **kw):
            def fun():
                return f(request, addon_id=addon.id, addon=addon, *args, **kw)

            if request.method in ('HEAD', 'GET'):
                # Allow reviewers for read operations, if file_id is present
                # and the reviewer is the right kind of reviewer for this file.
                if allow_reviewers_for_read:
                    file_id = kw.get('file_id')
                    if file_id:
                        is_unlisted = Version.unfiltered.filter(
                            files__id=file_id,
                            channel=amo.RELEASE_CHANNEL_UNLISTED).exists()
                        has_required_permission = (
                            acl.check_unlisted_addons_viewer_or_reviewer(
                                request) if is_unlisted else
                            (acl.check_listed_addons_viewer_or_reviewer(
                                request)))
                        if has_required_permission:
                            return fun()
                    else:
                        raise ImproperlyConfigured

                # On read-only requests, ignore disabled so developers can
                # still view their add-on.
                if acl.check_addon_ownership(request,
                                             addon,
                                             dev=not owner_for_get,
                                             ignore_disabled=True):
                    # Redirect to the submit flow if they're not done.
                    if not submitting and addon.should_redirect_to_submit_flow(
                    ):
                        return redirect('devhub.submit.details', addon.slug)
                    return fun()
            # Require an owner or dev for POST requests (if the add-on status
            # is disabled that check will return False).
            elif request.method == 'POST':
                if acl.check_addon_ownership(request,
                                             addon,
                                             dev=not owner_for_post):
                    return fun()
            raise PermissionDenied
Exemplo n.º 18
0
        def wrapper(request, addon, *args, **kw):
            def fun():
                return f(request, addon_id=addon.id, addon=addon, *args, **kw)

            if allow_reviewers:
                if acl.is_reviewer(request, addon):
                    return fun()
            # Require an owner or dev for POST requests.
            if request.method == 'POST':
                if acl.check_addon_ownership(request,
                                             addon,
                                             dev=not owner_for_post):
                    return fun()
            # Ignore disabled so they can view their add-on.
            elif acl.check_addon_ownership(request,
                                           addon,
                                           dev=True,
                                           ignore_disabled=True):
                # Redirect to the submit flow if they're not done.
                if (not submitting and addon.should_redirect_to_submit_flow()):
                    return redirect('devhub.submit.details', addon.slug)
                return fun()
            raise PermissionDenied
Exemplo n.º 19
0
def review_list(request, addon, review_id=None, user_id=None):
    qs = Review.without_replies.all().filter(addon=addon).order_by('-created')

    ctx = {'addon': addon, 'grouped_ratings': GroupedRating.get(addon.id)}

    ctx['form'] = forms.ReviewForm(None)
    is_admin = acl.action_allowed(request, amo.permissions.ADDONS_EDIT)

    if review_id is not None:
        ctx['page'] = 'detail'
        # If this is a dev reply, find the first msg for context.
        review = get_object_or_404(Review.objects.all(), pk=review_id)
        if review.reply_to_id:
            review_id = review.reply_to_id
            ctx['reply'] = review
        qs = qs.filter(pk=review_id)
    elif user_id is not None:
        ctx['page'] = 'user'
        qs = qs.filter(user=user_id)
        if not qs:
            raise http.Http404()
    else:
        ctx['page'] = 'list'
        qs = qs.filter(is_latest=True)
        # Don't filter out empty reviews for admins.
        if not is_admin:
            # But otherwise, filter out everyone elses empty reviews.
            user_filter = (Q(user=request.user.pk)
                           if request.user.is_authenticated() else Q())
            qs = qs.filter(~Q(body=None) | user_filter)

    ctx['reviews'] = reviews = paginate(request, qs)
    ctx['replies'] = Review.get_replies(reviews.object_list)
    if request.user.is_authenticated():
        ctx['review_perms'] = {
            'is_admin':
            is_admin,
            'is_editor':
            acl.is_editor(request, addon),
            'is_author':
            acl.check_addon_ownership(request,
                                      addon,
                                      viewer=True,
                                      dev=True,
                                      support=True),
        }
        ctx['flags'] = get_flags(request, reviews.object_list)
    else:
        ctx['review_perms'] = {}
    return render(request, 'reviews/review_list.html', ctx)
Exemplo n.º 20
0
def review_list(request, addon, review_id=None, user_id=None):
    qs = Rating.without_replies.all().filter(
        addon=addon).order_by('-created')

    ctx = {'addon': addon,
           'grouped_ratings': GroupedRating.get(addon.id)}

    ctx['form'] = forms.RatingForm(None)
    is_admin = acl.action_allowed(request, amo.permissions.ADDONS_EDIT)

    if review_id is not None:
        ctx['page'] = 'detail'
        # If this is a dev reply, find the first msg for context.
        review = get_object_or_404(Rating.objects.all(), pk=review_id)
        if review.reply_to_id:
            review_id = review.reply_to_id
            ctx['reply'] = review
        qs = qs.filter(pk=review_id)
    elif user_id is not None:
        ctx['page'] = 'user'
        qs = qs.filter(user=user_id)
        if not qs:
            raise http.Http404()
    else:
        ctx['page'] = 'list'
        qs = qs.filter(is_latest=True)
        # Don't filter out empty reviews for admins.
        if not is_admin:
            # But otherwise, filter out everyone elses empty reviews.
            user_filter = (Q(user=request.user.pk)
                           if request.user.is_authenticated() else Q())
            qs = qs.filter(~Q(body=None) | user_filter)

    ctx['reviews'] = reviews = paginate(request, qs)
    ctx['replies'] = Rating.get_replies(reviews.object_list)
    if request.user.is_authenticated():
        ctx['review_perms'] = {
            'is_admin': is_admin,
            'is_reviewer': acl.action_allowed(
                request, amo.permissions.RATINGS_MODERATE),
            'is_author': acl.check_addon_ownership(request, addon, viewer=True,
                                                   dev=True, support=True),
        }
        ctx['flags'] = get_flags(request, reviews.object_list)
    else:
        ctx['review_perms'] = {}
    return render(request, 'ratings/review_list.html', ctx)
Exemplo n.º 21
0
    def wrapper(*args, **kw):
        request = args[1]
        addon_id = kw['addon_id']
        try:
            addon = Addon.objects.id_or_slug(addon_id).get()
        except:
            return rc.NOT_HERE
        if not acl.check_addon_ownership(request, addon, viewer=True):
            return rc.FORBIDDEN

        if 'version_id' in kw:
            try:
                version = Version.objects.get(addon=addon, pk=kw['version_id'])
            except Version.DoesNotExist:
                return rc.NOT_HERE
            return f(*args, addon=addon, version=version)
        else:
            return f(*args, addon=addon)
Exemplo n.º 22
0
def review_list(request, addon, review_id=None, user_id=None, template=None):
    qs = Review.without_replies.all().filter(addon=addon).order_by('-created')

    ctx = {'addon': addon, 'grouped_ratings': GroupedRating.get(addon.id)}

    ctx['form'] = forms.ReviewForm(None)

    if review_id is not None:
        ctx['page'] = 'detail'
        # If this is a dev reply, find the first msg for context.
        review = get_object_or_404(Review.objects.all(), pk=review_id)
        if review.reply_to_id:
            review_id = review.reply_to_id
            ctx['reply'] = review
        qs = qs.filter(pk=review_id)
    elif user_id is not None:
        ctx['page'] = 'user'
        qs = qs.filter(user=user_id)
        if not qs:
            raise http.Http404()
    else:
        ctx['page'] = 'list'
        qs = qs.filter(is_latest=True)

    ctx['reviews'] = reviews = paginate(request, qs)
    ctx['replies'] = Review.get_replies(reviews.object_list)
    if request.user.is_authenticated():
        ctx['review_perms'] = {
            'is_admin':
            acl.action_allowed(request, 'Addons', 'Edit'),
            'is_editor':
            acl.is_editor(request, addon),
            'is_author':
            acl.check_addon_ownership(request,
                                      addon,
                                      viewer=True,
                                      dev=True,
                                      support=True),
        }
        ctx['flags'] = get_flags(request, reviews.object_list)
    else:
        ctx['review_perms'] = {}
    return render(request, template, ctx)
Exemplo n.º 23
0
def allowed(request, file):
    try:
        addon = file.version.addon
    except ObjectDoesNotExist:
        raise http.Http404

    # General case: addon is listed.
    if addon.is_listed:
        if ((addon.view_source and addon.status in amo.REVIEWED_STATUSES) or
                acl.check_addons_reviewer(request) or
                acl.check_addon_ownership(request, addon, viewer=True,
                                          dev=True)):
            return True  # Public and sources are visible, or reviewer.
        raise PermissionDenied  # Listed but not allowed.
    # Not listed? Needs an owner or an "unlisted" admin.
    else:
        if owner_or_unlisted_reviewer(request, addon):
            return True
    raise http.Http404  # Not listed, not owner or admin.
Exemplo n.º 24
0
def allowed(request, file):
    try:
        addon = file.version.addon
    except ObjectDoesNotExist:
        raise http.Http404

    # General case: addon is listed.
    if addon.is_listed:
        if ((addon.view_source and addon.status in amo.REVIEWED_STATUSES)
                or acl.check_addons_reviewer(request)
                or acl.check_addon_ownership(
                    request, addon, viewer=True, dev=True)):
            return True  # Public and sources are visible, or reviewer.
        raise PermissionDenied  # Listed but not allowed.
    # Not listed? Needs an owner or an "unlisted" admin.
    else:
        if owner_or_unlisted_reviewer(request, addon):
            return True
    raise http.Http404  # Not listed, not owner or admin.
Exemplo n.º 25
0
def download_source(request, version_id):
    version = get_object_or_404(Version.objects, pk=version_id)

    # General case: version is listed.
    if version.channel == amo.RELEASE_CHANNEL_LISTED:
        if not (version.source and (acl.check_addon_ownership(
                request, version.addon, dev=True, ignore_disabled=True))):
            raise http.Http404()
    else:
        if not owner_or_unlisted_reviewer(request, version.addon):
            raise http.Http404  # Not listed, not owner or unlisted reviewer.
    res = HttpResponseSendFile(request, version.source.path)
    path = version.source.path
    if not isinstance(path, six.text_type):
        path = path.decode('utf8')
    name = os.path.basename(path.replace(u'"', u''))
    disposition = u'attachment; filename="{0}"'.format(name).encode('utf8')
    res['Content-Disposition'] = disposition
    return res
Exemplo n.º 26
0
def reply(request, addon, review_id):
    is_admin = acl.action_allowed(request, amo.permissions.ADDONS_EDIT)
    is_author = acl.check_addon_ownership(request, addon, dev=True)
    if not (is_admin or is_author):
        raise PermissionDenied

    review = get_object_or_404(Review.objects, pk=review_id, addon=addon)
    form = forms.ReviewReplyForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        kwargs = {
            'reply_to': review,
            'addon': addon,
            'defaults': _review_details(request, addon, form)
        }
        reply, created = Review.unfiltered.update_or_create(**kwargs)
        return redirect(
            jinja_helpers.url('addons.reviews.detail', addon.slug, review_id))
    ctx = {'review': review, 'form': form, 'addon': addon}
    return render(request, 'reviews/reply.html', ctx)
Exemplo n.º 27
0
def reply(request, addon, review_id):
    is_admin = acl.action_allowed(request, 'Addons', 'Edit')
    is_author = acl.check_addon_ownership(request, addon, dev=True)
    if not (is_admin or is_author):
        raise PermissionDenied

    review = get_object_or_404(Review.objects, pk=review_id, addon=addon)
    form = forms.ReviewReplyForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        kwargs = {
            'reply_to': review,
            'addon': addon,
            'defaults': _review_details(request, addon, form)
        }
        reply, created = Review.unfiltered.update_or_create(**kwargs)
        return redirect(helpers.url('addons.reviews.detail', addon.slug,
                                    review_id))
    ctx = dict(review=review, form=form, addon=addon)
    return render(request, 'reviews/reply.html', ctx)
Exemplo n.º 28
0
def download_source(request, version_id):
    """
    Download source code for a given version_id.

    Requires developer of the add-on or admin reviewer permission. If the
    version or add-on is deleted, developers can't access.

    If the version source code wasn't provided, but the user had the right
    permissions, a 404 is raised.
    """
    # Include deleted versions in the queryset, we'll check for that below.
    version = get_object_or_404(Version.unfiltered, pk=version_id)
    addon = version.addon

    # Channel doesn't matter, source code is only available to admin reviewers
    # or developers of the add-on. If the add-on, version or file is deleted or
    # disabled, then only admins can access.
    has_permission = acl.action_allowed(request, amo.permissions.REVIEWS_ADMIN)

    if (
        addon.status != amo.STATUS_DISABLED
        and not version.files.filter(status=amo.STATUS_DISABLED).exists()
        and not version.deleted
        and not addon.is_deleted
    ):
        # Don't rely on 'admin' parameter for check_addon_ownership(), it
        # doesn't check the permission we want to check.
        has_permission = has_permission or acl.check_addon_ownership(
            request, addon, admin=False, dev=True
        )
    if not has_permission:
        raise http.Http404()

    response = HttpResponseXSendFile(request, version.source.path)
    path = version.source.path
    if not isinstance(path, str):
        path = path.decode('utf8')
    name = os.path.basename(path.replace('"', ''))
    disposition = 'attachment; filename="{0}"'.format(name).encode('utf8')
    response['Content-Disposition'] = disposition
    response['Access-Control-Allow-Origin'] = '*'
    return response
Exemplo n.º 29
0
def download_source(request, version_id):
    version = get_object_or_404(Version.objects, pk=version_id)

    # General case: version is listed.
    if version.channel == amo.RELEASE_CHANNEL_LISTED:
        if not (version.source and
                (acl.check_addon_ownership(
                    request, version.addon, dev=True, ignore_disabled=True))):
            raise http.Http404()
    else:
        if not owner_or_unlisted_reviewer(request, version.addon):
            raise http.Http404  # Not listed, not owner or unlisted reviewer.
    res = HttpResponseSendFile(request, version.source.path)
    path = version.source.path
    if not isinstance(path, six.text_type):
        path = path.decode('utf8')
    name = os.path.basename(path.replace(u'"', u''))
    disposition = u'attachment; filename="{0}"'.format(name).encode('utf8')
    res['Content-Disposition'] = disposition
    return res
Exemplo n.º 30
0
def allowed(request, file):
    try:
        version = file.version
        addon = version.addon
    except ObjectDoesNotExist:
        raise http.Http404

    # General case: addon is listed.
    if version.channel == amo.RELEASE_CHANNEL_LISTED:
        # We don't show the file-browser publicly because of potential DOS
        # issues, we're working on a fix but for now, let's not do this.
        # (cgrebs, 06042017)
        is_owner = acl.check_addon_ownership(request, addon, dev=True)
        if (acl.is_reviewer(request, addon) or is_owner):
            return True  # Public and sources are visible, or reviewer.
        raise PermissionDenied  # Listed but not allowed.
    # Not listed? Needs an owner or an "unlisted" admin.
    else:
        if owner_or_unlisted_reviewer(request, addon):
            return True
    raise http.Http404  # Not listed, not owner or admin.
Exemplo n.º 31
0
def download_source(request, version_id):
    version = get_object_or_404(Version, pk=version_id)

    # General case: addon is listed.
    if version.addon.is_listed:
        if not (version.source and
                (acl.check_addon_ownership(request, version.addon,
                                           viewer=True, ignore_disabled=True)
                 or acl.action_allowed(request, 'Editors', 'BinarySource'))):
            raise http.Http404()
    else:
        if not owner_or_unlisted_reviewer(request, version.addon):
            raise http.Http404  # Not listed, not owner or admin.
    res = HttpResponseSendFile(request, version.source.path)
    path = version.source.path
    if not isinstance(path, unicode):
        path = path.decode('utf8')
    name = os.path.basename(path.replace(u'"', u''))
    disposition = u'attachment; filename="{0}"'.format(name).encode('utf8')
    res['Content-Disposition'] = disposition
    return res
Exemplo n.º 32
0
def allowed(request, file):
    try:
        version = file.version
        addon = version.addon
    except ObjectDoesNotExist:
        raise http.Http404

    # General case: addon is listed.
    if version.channel == amo.RELEASE_CHANNEL_LISTED:
        # We don't show the file-browser publicly because of potential DOS
        # issues, we're working on a fix but for now, let's not do this.
        # (cgrebs, 06042017)
        is_owner = acl.check_addon_ownership(request, addon, dev=True)
        if (acl.is_reviewer(request, addon) or is_owner):
            return True  # Public and sources are visible, or reviewer.
        raise PermissionDenied  # Listed but not allowed.
    # Not listed? Needs an owner or an "unlisted" admin.
    else:
        if owner_or_unlisted_reviewer(request, addon):
            return True
    raise http.Http404  # Not listed, not owner or admin.
Exemplo n.º 33
0
def download_source(request, version_id):
    version = get_object_or_404(Version, pk=version_id)

    # General case: addon is listed.
    if version.addon.is_listed:
        if not (version.source and
                (acl.check_addon_ownership(
                    request, version.addon, viewer=True, ignore_disabled=True)
                 or acl.action_allowed(request, 'Editors', 'BinarySource'))):
            raise http.Http404()
    else:
        if not owner_or_unlisted_reviewer(request, version.addon):
            raise http.Http404  # Not listed, not owner or admin.
    res = HttpResponseSendFile(request, version.source.path)
    path = version.source.path
    if not isinstance(path, unicode):
        path = path.decode('utf8')
    name = os.path.basename(path.replace(u'"', u''))
    disposition = u'attachment; filename="{0}"'.format(name).encode('utf8')
    res['Content-Disposition'] = disposition
    return res
Exemplo n.º 34
0
def reply(request, addon, review_id):
    is_admin = acl.action_allowed(request, 'Addons', 'Edit')
    is_author = acl.check_addon_ownership(request, addon, dev=True)
    if not (is_admin or is_author):
        raise PermissionDenied

    review = get_object_or_404(Review.objects, pk=review_id, addon=addon)
    form = forms.ReviewReplyForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        d = dict(reply_to=review,
                 addon=addon,
                 defaults=dict(user=request.user))
        reply, new = Review.objects.get_or_create(**d)
        for key, val in _review_details(request, addon, form).items():
            setattr(reply, key, val)
        reply.save()
        action = 'New' if new else 'Edited'
        log.debug('%s reply to %s: %s' % (action, review_id, reply.id))

        if new:
            reply_url = helpers.url('addons.reviews.detail',
                                    addon.slug,
                                    review.id,
                                    add_prefix=False)
            data = {
                'name': addon.name,
                'reply_title': reply.title,
                'reply': reply.body,
                'reply_url': helpers.absolutify(reply_url)
            }
            emails = [review.user.email]
            sub = u'Mozilla Add-on Developer Reply: %s' % addon.name
            send_mail('reviews/emails/reply_review.ltxt', sub, emails,
                      Context(data), 'reply')

        return redirect(
            helpers.url('addons.reviews.detail', addon.slug, review_id))
    ctx = dict(review=review, form=form, addon=addon)
    return render(request, 'reviews/reply.html', ctx)
Exemplo n.º 35
0
def review_list(request, addon, review_id=None, user_id=None, template=None):
    q = (Review.objects.valid().filter(addon=addon)
         .order_by('-created'))

    ctx = {'addon': addon,
           'grouped_ratings': GroupedRating.get(addon.id)}

    ctx['form'] = forms.ReviewForm(None)

    if review_id is not None:
        ctx['page'] = 'detail'
        # If this is a dev reply, find the first msg for context.
        review = get_object_or_404(Review.objects.all(), pk=review_id)
        if review.reply_to_id:
            review_id = review.reply_to_id
            ctx['reply'] = review
        q = q.filter(pk=review_id)
    elif user_id is not None:
        ctx['page'] = 'user'
        q = q.filter(user=user_id)
        if not q:
            raise http.Http404()
    else:
        ctx['page'] = 'list'
        q = q.filter(is_latest=True)

    ctx['reviews'] = reviews = amo_utils.paginate(request, q)
    ctx['replies'] = Review.get_replies(reviews.object_list)
    if request.user.is_authenticated():
        ctx['review_perms'] = {
            'is_admin': acl.action_allowed(request, 'Addons', 'Edit'),
            'is_editor': acl.is_editor(request, addon),
            'is_author': acl.check_addon_ownership(request, addon, viewer=True,
                                                   dev=True, support=True),
        }
        ctx['flags'] = get_flags(request, reviews.object_list)
    else:
        ctx['review_perms'] = {}
    return render(request, template, ctx)
Exemplo n.º 36
0
def reply(request, addon, review_id):
    is_admin = acl.action_allowed(request, amo.permissions.ADDONS_EDIT)
    is_author = acl.check_addon_ownership(request, addon, dev=True)
    if not (is_admin or is_author):
        raise DjangoPermissionDenied

    rating = get_object_or_404(Rating.objects, pk=review_id, addon=addon)
    form = forms.RatingReplyForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        kwargs = {
            'reply_to': rating,
            'addon': addon,
            'defaults': _review_details(request, addon, form)
        }
        reply, created = Rating.unfiltered.update_or_create(**kwargs)
        return redirect(jinja_helpers.url(
            'addons.ratings.detail', addon.slug, review_id))
    ctx = {
        'review': rating,
        'form': form,
        'addon': addon
    }
    return render(request, 'ratings/reply.html', ctx)
Exemplo n.º 37
0
def download_file(request, file_id, type=None, file_=None, addon=None):
    """
    Download given file.

    `addon` and `file_` parameters can be passed to avoid the database query.

    If the file is disabled or belongs to an unlisted version, requires an
    add-on developer or appropriate reviewer for the channel. If the file is
    deleted or belongs to a deleted version or add-on, reviewers can still
    access but developers can't.
    """
    def is_appropriate_reviewer(addon, channel):
        return (acl.is_reviewer(request, addon)
                if channel == amo.RELEASE_CHANNEL_LISTED else
                acl.check_unlisted_addons_viewer_or_reviewer(request))

    if not file_:
        file_ = get_object_or_404(File.objects, pk=file_id)
    if not addon:
        # Include deleted add-ons in the queryset, we'll check for that below.
        addon = get_object_or_404(Addon.unfiltered, pk=file_.version.addon_id)
    version = file_.version
    channel = version.channel

    if version.deleted or addon.is_deleted:
        # Only the appropriate reviewer can see deleted things.
        use_cdn = False
        has_permission = is_appropriate_reviewer(addon, channel)
    elif (addon.is_disabled or file_.status == amo.STATUS_DISABLED
          or channel == amo.RELEASE_CHANNEL_UNLISTED):
        # Only the appropriate reviewer or developers of the add-on can see
        # disabled or unlisted things.
        use_cdn = False
        has_permission = is_appropriate_reviewer(
            addon, channel) or acl.check_addon_ownership(
                request,
                addon,
                allow_developer=True,
                allow_mozilla_disabled_addon=True,
                allow_site_permission=True,
            )
    else:
        # Everyone can see public things, and we can use the CDN in that case.
        use_cdn = True
        has_permission = True

    if not has_permission:
        log.debug('download file {file_id}: addon/version/file not public and '
                  'user {user_id} does not have relevant permissions.'.format(
                      file_id=file_id, user_id=request.user.pk))
        raise http.Http404()  # Not owner or admin.

    attachment = bool(type == 'attachment')
    if use_cdn:
        # When serving the file for the general public through the CDN, we need
        # to obey regional restrictions
        region_code = request.META.get('HTTP_X_COUNTRY_CODE', None)
        if (region_code
                and AddonRegionalRestrictions.objects.filter(
                    addon=addon,
                    excluded_regions__contains=region_code.upper()).exists()):
            response = http.HttpResponse(status=451)
            url = 'https://www.mozilla.org/about/policy/transparency/'
            response['Link'] = f'<{url}>; rel="blocked-by"'
        else:
            # When using the CDN URL, we do a redirect, so we can't set
            # Content-Disposition: attachment for attachments. To work around
            # this, if attachment=True, get_file_cdn_url() changes the path to
            # something we recognize in the nginx config.
            loc = urlparams(file_.get_file_cdn_url(attachment=attachment),
                            filehash=file_.hash)
            response = http.HttpResponseRedirect(loc)
            response['X-Target-Digest'] = file_.hash
        # Always add a Vary header to deal with caching in different regions.
        patch_vary_headers(response, ['X-Country-Code'])
    else:
        # Here we're returning a X-Accel-Redirect, we can set
        # Content-Disposition: attachment ourselves in HttpResponseXSendFile:
        # nginx won't override it if present.
        response = HttpResponseXSendFile(
            request,
            file_.current_file_path,
            content_type='application/x-xpinstall',
            attachment=attachment,
        )
    response['Access-Control-Allow-Origin'] = '*'
    return response
Exemplo n.º 38
0
def download_file(request, file_id, download_type=None, **kwargs):
    """
    Download the file identified by `file_id` parameter.

    If the file is disabled or belongs to an unlisted version, requires an
    add-on developer or appropriate reviewer for the channel. If the file is
    deleted or belongs to a deleted version or add-on, reviewers can still
    access but developers can't.
    """

    def is_appropriate_reviewer(addon, channel):
        return (
            acl.is_reviewer(request, addon)
            if channel == amo.RELEASE_CHANNEL_LISTED
            else acl.check_unlisted_addons_viewer_or_reviewer(request)
        )

    file_ = get_object_or_404(File.objects, pk=file_id)
    # Include deleted add-ons in the queryset, we'll check for that below.
    addon = get_object_or_404(
        Addon.unfiltered.all().no_transforms(), pk=file_.version.addon_id
    )
    version = file_.version
    channel = version.channel

    if version.deleted or addon.is_deleted:
        # Only the appropriate reviewer can see deleted things.
        has_permission = is_appropriate_reviewer(addon, channel)
        apply_georestrictions = False
    elif (
        addon.is_disabled
        or file_.status == amo.STATUS_DISABLED
        or channel == amo.RELEASE_CHANNEL_UNLISTED
    ):
        # Only the appropriate reviewer or developers of the add-on can see
        # disabled or unlisted things.
        has_permission = is_appropriate_reviewer(
            addon, channel
        ) or acl.check_addon_ownership(
            request,
            addon,
            allow_developer=True,
            allow_mozilla_disabled_addon=True,
            allow_site_permission=True,
        )
        apply_georestrictions = False
    else:
        # Public case: we're either directly downloading the file or
        # redirecting, but in any case we have permission in the general sense,
        # though georestrictions are in effect.
        has_permission = True
        apply_georestrictions = True

    region_code = request.META.get('HTTP_X_COUNTRY_CODE', None)
    # Whether to set Content-Disposition: attachment header or not, to force
    # the file to be downloaded rather than installed (used by admin/reviewer
    # tools).
    attachment = download_type == 'attachment'
    if not has_permission:
        log.debug(
            'download file {file_id}: addon/version/file not public and '
            'user {user_id} does not have relevant permissions.'.format(
                file_id=file_id, user_id=request.user.pk
            )
        )
        response = http.HttpResponseNotFound()
    elif (
        apply_georestrictions
        and region_code
        and AddonRegionalRestrictions.objects.filter(
            addon=addon, excluded_regions__contains=region_code.upper()
        ).exists()
    ):
        response = http.HttpResponse(status=451)
        url = 'https://www.mozilla.org/about/policy/transparency/'
        response['Link'] = f'<{url}>; rel="blocked-by"'
    else:
        # We're returning a X-Accel-Redirect, we can set
        # Content-Disposition: attachment ourselves in HttpResponseXSendFile:
        # nginx won't override it if present.
        response = HttpResponseXSendFile(
            request,
            file_.current_file_path,
            content_type='application/x-xpinstall',
            attachment=attachment,
        )
    # Always add a few headers to the response (even errors).
    patch_cache_control(response, max_age=60 * 60 * 24)
    patch_vary_headers(response, ['X-Country-Code'])
    response['Access-Control-Allow-Origin'] = '*'
    return response