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
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
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
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
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
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
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
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
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
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
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
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))
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
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))
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)
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
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
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)
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)
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)
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)
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.
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.
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
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)
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)
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
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.
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
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
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)
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)
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)
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
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