Esempio n. 1
0
def sitemap(request):
    section = request.GET.get('section')  # no section means the index page
    app = request.GET.get('app_name')
    page = request.GET.get('p', 1)
    if 'debug' in request.GET and settings.SITEMAP_DEBUG_AVAILABLE:
        try:
            sitemaps = get_sitemaps()
            if not section:
                if page != 1:
                    raise EmptyPage
                content = render_index_xml(sitemaps)
            else:
                sitemap_object = sitemaps.get((section, amo.APPS.get(app)))
                if not sitemap_object:
                    raise InvalidSection
                content = sitemap_object.render_xml(app, page)
        except EmptyPage:
            raise Http404('Page %s empty' % page)
        except PageNotAnInteger:
            raise Http404('No page "%s"' % page)
        except InvalidSection:
            raise Http404('No sitemap available for section: %r' % section)
        response = HttpResponse(content, content_type='application/xml')
    else:
        path = get_sitemap_path(section, app, page)
        response = HttpResponseXSendFile(request,
                                         path,
                                         content_type='application/xml')
        patch_cache_control(response, max_age=60 * 60)
    return response
Esempio n. 2
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
Esempio n. 3
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 HttpResponseXSendFile(
                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 HttpResponseXSendFile(
                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
Esempio n. 4
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 = HttpResponseXSendFile(request, version.source.path)
    path = version.source.path
    if not isinstance(path, str):
        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
Esempio n. 5
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
Esempio n. 6
0
def serve_file_upload(request, uuid):
    """
    This is to serve file uploads using authenticated download URLs. This is
    currently used by the "scanner" services.
    """
    upload = shortcuts.get_object_or_404(FileUpload, uuid=uuid)
    access_token = request.GET.get('access_token')

    if not access_token:
        log.error('Denying access to %s, no token.', upload.id)
        raise PermissionDenied

    if not constant_time_compare(access_token, upload.access_token):
        log.error('Denying access to %s, token invalid.', upload.id)
        raise PermissionDenied

    if not upload.path:
        log.info('Preventing access to %s, upload path is falsey.' % upload.id)
        return http.HttpResponseGone('upload path does not exist anymore')

    return HttpResponseXSendFile(
        request, upload.path, content_type='application/octet-stream'
    )
Esempio n. 7
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
Esempio n. 8
0
 def test_adds_content_disposition_header(self):
     response = HttpResponseXSendFile(request=None,
                                      path='/',
                                      attachment=True)
     assert response.has_header('Content-Disposition')
     assert response['Content-Disposition'] == 'attachment'
Esempio n. 9
0
 def test_adds_etag_header(self):
     etag = '123'
     response = HttpResponseXSendFile(request=None, path='/', etag=etag)
     assert response.has_header('ETag')
     assert response['ETag'] == quote_etag(etag)
Esempio n. 10
0
 def test_normalizes_path(self):
     path = '/some/../path/'
     response = HttpResponseXSendFile(request=None, path=path)
     assert response[settings.XSENDFILE_HEADER] == os.path.normpath(path)
     assert not response.has_header('Content-Disposition')
Esempio n. 11
0
def download_file_upload(request, uuid):
    upload = get_object_or_404(FileUpload, uuid=uuid)

    return HttpResponseXSendFile(request,
                                 upload.path,
                                 content_type='application/octet-stream')
Esempio n. 12
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
Esempio n. 13
0
 def test_normalizes_path(self):
     path = '/some/../path/'
     resp = HttpResponseXSendFile(request=None, path=path)
     assert resp[settings.XSENDFILE_HEADER] == os.path.normpath(path)