Ejemplo n.º 1
0
    def _raise_304_if_not_modified(self, record=None):
        """Raise 304 if current timestamp is inferior to the one specified
        in headers.

        :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPNotModified`
        """
        if_none_match = self.request.headers.get('If-None-Match')

        if not if_none_match:
            return

        if_none_match = decode_header(if_none_match)

        try:
            if not (if_none_match[0] == if_none_match[-1] == '"'):
                raise ValueError()
            modified_since = int(if_none_match[1:-1])
        except (IndexError, ValueError):
            if if_none_match == '*':
                return
            error_details = {
                'location': 'headers',
                'description': "Invalid value for If-None-Match"
            }
            raise_invalid(self.request, **error_details)

        if record:
            current_timestamp = record[self.model.modified_field]
        else:
            current_timestamp = self.model.timestamp()

        if current_timestamp <= modified_since:
            response = HTTPNotModified()
            self._add_timestamp_header(response, timestamp=current_timestamp)
            raise response
Ejemplo n.º 2
0
def check_precondition_headers(viewfunc, request):
    """View decorator to check X-If-[Unm|M]odified-Since headers.

    This decorator checks pre-validated vlaues from the X-If-Modified-Since
    and X-If-Unmodified-Since headers against the actual last-modified
    time of the target resource.  If the preconditions are not met then
    it raises the appropriate error response.

    In addition, any retreived value for the last-modified time will be
    stored in the response headers for return to the client.  This may save
    having to look it up again when the response is being rendered.
    """
    if "if_modified_since" in request.validated:
        ts = get_resource_timestamp(request)
        request.response.headers["X-Last-Modified"] = str(ts)
        if ts <= request.validated["if_modified_since"]:
            raise HTTPNotModified(headers={
                "X-Last-Modified": str(ts),
            })

    if "if_unmodified_since" in request.validated:
        ts = get_resource_timestamp(request)
        request.response.headers["X-Last-Modified"] = str(ts)
        if ts > request.validated["if_unmodified_since"]:
            raise HTTPPreconditionFailed(headers={
                "X-Last-Modified": str(ts),
            })

    return viewfunc(request)
Ejemplo n.º 3
0
 def _check_condition_none_match(self):
     self.set_etag()
     etag = self.request.response.etag
     if etag is None:
         return
     if etag == self.request.if_none_match.etags[0]:  # pragma: no branch
         raise HTTPNotModified()
Ejemplo n.º 4
0
def etag_cache(request, etag_key):
    """Use the HTTP Entity Tag cache for Browser side caching
    If a "If-None-Match" header is found, and equivalent to ``key``,
    then a ``304`` HTTP message will be returned with the ETag to tell
    the browser that it should use its current cache of the page.
    Otherwise, the ETag header will be added to the response headers.
    Suggested use is within a view like so:
    .. code-block:: python
        def view(request):
            etag_cache(request, key=1)
            return render('/splash.mako')
    .. note::
        This works because etag_cache will raise an HTTPNotModified
        exception if the ETag received matches the key provided.

    Implementation adapted from:
    https://github.com/Pylons/pylons/blob/799c310/pylons/controllers/util.py#L148  # noqa
    """
    # we are always using a weak ETag validator
    etag = 'W/"%s"' % etag_key
    etag_matcher = request.if_none_match

    if str(etag_key) in etag_matcher:
        headers = [('ETag', etag)]
        log.debug("ETag match, returning 304 HTTP Not Modified Response")
        raise HTTPNotModified(headers=headers)
    else:
        request.response.headers['ETag'] = etag
        log.debug("ETag didn't match, returning response object")
Ejemplo n.º 5
0
def check_etag(request, etag):
    etag = str(etag)
    if etag in request.if_none_match:
        raise HTTPNotModified()
    request.response.vary = 'Cookie'
    request.response.cache_control = 'max-age=3600, private, must-revalidate'
    request.response.etag = etag
Ejemplo n.º 6
0
 def wrapped(context, request):
     etag = request.registry.settings['snovault.app_version']
     if etag in request.if_none_match:
         raise HTTPNotModified()
     result = view_callable(context, request)
     request.response.etag = etag
     return result
Ejemplo n.º 7
0
def view_asset(request):
    """Handles the ``/parts/{pid}/files/name/assets/{filename}``
    URL, sending back the correct :class:`~wte.models.Asset`.

    Requires that the user has "view" rights on the :class:`~wte.models.Part`.
    """
    dbsession = DBSession()
    part = dbsession.query(Part).filter(
        Part.id == request.matchdict['pid']).first()
    if part.type == 'page':
        part = part.parent
    asset = dbsession.query(Asset).join(Part.assets).\
        filter(and_(Asset.filename == request.matchdict['filename'],
                    Part.id == part.id)).first()
    if part and asset:
        if part.allow('view', request.current_user):
            if 'If-None-Match' in request.headers and request.headers[
                    'If-None-Match'] == asset.etag:
                raise HTTPNotModified()
            headerlist = [('Content-Type', str(asset.mimetype))]
            if asset.etag is not None:
                headerlist.append(('ETag', str(asset.etag)))
            if 'download' in request.params:
                if request.params['download'].lower() == 'true':
                    headerlist.append(
                        ('Content-Disposition',
                         str('attachment; filename="%s"' % (asset.filename))))
            return Response(body=asset.data, headerlist=headerlist)
        else:
            unauthorised_redirect(request)
    else:
        raise HTTPNotFound()
Ejemplo n.º 8
0
def view_file(request):
    """Handles the ``parts/{ptid}/pages/{pid}/users/{uid}/files/name/{filename}``
    URL, sending back the correct :class:`~wte.models.Asset`.

    Requires that the user has "view" rights on the :class:`~wte.models.Part`.
    It will also only send an :class:`~wte.models.Asset` belonging to the current
    :class:`~wte.models.User`.
    """
    dbsession = DBSession()
    part = dbsession.query(Part).filter(
        Part.id == request.matchdict['pid']).first()
    if part:
        if part.allow('view', request.current_user):
            progress = get_user_part_progress(dbsession, request.current_user,
                                              part)
            for user_file in progress.files:
                if user_file.filename == request.matchdict['filename']:
                    if 'If-None-Match' in request.headers and request.headers[
                            'If-None-Match'] == user_file.etag:
                        raise HTTPNotModified()
                    headers = [('Content-Type', str(user_file.mimetype))]
                    if user_file.etag is not None:
                        headers.append(('ETag', str(user_file.etag)))
                    if 'download' in request.params:
                        headers.append(('Content-Disposition',
                                        str('attachment; filename="%s"' %
                                            (user_file.filename))))
                    return Response(body=user_file.data, headerlist=headers)
            raise HTTPNotFound()
        else:
            unauthorised_redirect(request)
    else:
        raise HTTPNotFound()
Ejemplo n.º 9
0
def test_process_conditional_put_requests(web_app, pyramid_request, etag,
                                          if_match, if_none_match,
                                          status_code):
    root = pyramid_request.root
    root['resource'] = resource = DummyResource({'foo': 'Hello', 'bar': 123})
    resource.etag = etag
    resource_url = pyramid_request.resource_url(root['resource'])

    headers = {}
    if if_match:
        headers['If-Match'] = if_match
    if if_none_match:
        headers['If-None-Match'] = if_none_match

    kwargs = {'headers': headers}
    if status_code == 304:
        kwargs['exception'] = HTTPNotModified()
    elif status_code == 412:
        kwargs['exception'] = HTTPPreconditionFailed(
            {'etag': None if etag is None else etag.serialize()})
    else:
        kwargs['status'] = status_code

    new_params = {'foo': 'World', 'bar': 456}
    web_app.put_json(resource_url, params=new_params, **kwargs)
Ejemplo n.º 10
0
 def _check_condition_modified_since(self):
     self.set_last_modified()
     last_modified = self.request.response.last_modified
     if last_modified is None:  # pragma: no coverage
         return
     modified_since = self.request.if_modified_since
     if last_modified <= modified_since:  # pragma: no branch
         raise HTTPNotModified()
Ejemplo n.º 11
0
 def wrapped(context, request):
     app_version = request.registry.settings['snovault.app_version']
     etag = app_version + ' ' + ' '.join(sorted(request.effective_principals))
     if etag in request.if_none_match:
         raise HTTPNotModified()
     result = view_callable(context, request)
     request.response.etag = etag
     cache_control = request.response.cache_control
     cache_control.private = True
     cache_control.max_age = 0
     cache_control.must_revalidate = True
     return result
Ejemplo n.º 12
0
        def notmodified_tween(request):
            if os.path.isfile(lock_file):
                response = Response(
                    render('templates/maintenance.jinja2', {}, request))
                response.status_code = 503
                return response

            if (request.if_modified_since is not None
                    and request.if_modified_since >=
                    publication_date.replace(microsecond=0)):
                return HTTPNotModified()

            response = handler(request)

            response.last_modified = publication_date

            return response
Ejemplo n.º 13
0
 def wrapped(context, request):
     if len(manager.stack) != 1:
         return view_callable(context, request)
     format = request.environ.get('encoded.format', 'html')
     session = DBSession()
     last_tid = session.query(func.max(TransactionRecord.order)).scalar()
     processid = request.registry['encoded.processid']
     userid = authenticated_userid(request) or ''
     etag = u'%s;%s;%s;%s' % (last_tid, processid, format, userid)
     etag = quote(etag.encode('utf-8'), ';:@')
     if etag in request.if_none_match:
         raise HTTPNotModified()
     result = view_callable(context, request)
     request.response.etag = etag
     cache_control = request.response.cache_control
     cache_control.private = True
     cache_control.max_age = 0
     cache_control.must_revalidate = True
     return result
Ejemplo n.º 14
0
        def notmodified_tween(request):
            if request.path == "/healthcheck":
                return handler(request)

            publication_date = gmt.localize(
                Publication.last(request.dbsession).date)

            if False:
                response = Response(
                    render("templates/maintenance.jinja2", {}, request))
                response.status_code = 503
                return response

            if (request.if_modified_since is not None
                    and request.if_modified_since >=
                    publication_date.replace(microsecond=0)):
                return HTTPNotModified()

            request.publication_date = publication_date
            response = handler(request)
            response.last_modified = publication_date

            return response
Ejemplo n.º 15
0
    def _raise_304_if_not_modified(self, record=None):
        """Raise 304 if current timestamp is inferior to the one specified
        in headers.

        :raises: :exc:`~pyramid:pyramid.httpexceptions.HTTPNotModified`
        """
        if_none_match = self.request.validated['header'].get('If-None-Match')

        if not if_none_match:
            return

        if if_none_match == '*':
            return

        if record:
            current_timestamp = record[self.model.modified_field]
        else:
            current_timestamp = self.model.timestamp()

        if current_timestamp == if_none_match:
            response = HTTPNotModified()
            self._add_timestamp_header(response, timestamp=current_timestamp)
            raise response
Ejemplo n.º 16
0
    def mapped_view(context, request: PyramidRequest):
        if context is not request.root:
            if_match = request.if_match
            if_none_match = request.if_none_match
            if if_match is not AnyETag or if_none_match is not NoETag:
                etag = context.get_etag()
                if etag is None:
                    if None not in if_match:
                        raise HTTPPreconditionFailed({'etag': None})
                else:
                    # https://tools.ietf.org/html/rfc7232#section-6
                    # https://tools.ietf.org/html/rfc7232#section-2.3.2
                    if if_match is not AnyETag:
                        if not etag.is_strict or etag.value not in if_match:
                            raise HTTPPreconditionFailed(
                                {'etag': etag.serialize()})
                    if etag.value in if_none_match:
                        if request.method in ('GET', 'HEAD'):
                            raise HTTPNotModified()
                        raise HTTPPreconditionFailed(
                            {'etag': etag.serialize()})

        return view(context, request)
Ejemplo n.º 17
0
def get_blocklist(request):
    prefix = request.matchdict['prefix']
    api_ver = int(request.matchdict['api_ver'])
    app = request.matchdict['application_guid']
    app_ver = request.matchdict['application_ver']

    # 1. Verify that we have a config for that prefix
    if prefix not in request.registry.amo_resources:
        raise HTTPNotFound()

    # Addons blocklist
    addons_records, addons_last_modified = get_records(request, prefix,
                                                       'addons')
    # Plugins blocklist
    plugins_records, plugins_last_modified = get_records(
        request, prefix, 'plugins')
    # GFX blocklist
    gfx_records, gfx_last_modified = get_records(request, prefix, 'gfx')
    # Certificates blocklist
    cert_records, cert_last_modified = get_records(request, prefix,
                                                   'certificates')

    # Expose highest timestamp in response headers.
    last_update = max(addons_last_modified, plugins_last_modified,
                      gfx_last_modified, cert_last_modified)
    last_etag = '"{}"'.format(last_update)
    request.response.headers['ETag'] = last_etag
    request.response.last_modified = last_update / 1000.0

    if_none_match = request.headers.get('If-None-Match')
    if_modified_since = request.headers.get('If-Modified-Since')
    if if_none_match is not None or if_modified_since is not None:
        has_changed = (
            if_none_match != last_etag
            and request.if_modified_since != request.response.last_modified)
        if not has_changed:
            response = HTTPNotModified()
            response.headers['ETag'] = last_etag
            response.last_modified = last_update / 1000.0
            raise response

    xml_tree = etree.Element(
        'blocklist',
        xmlns="http://www.mozilla.org/2006/addons-blocklist",
        lastupdate='%s' % last_update)

    write_addons_items(xml_tree,
                       addons_records,
                       api_ver=api_ver,
                       app_id=app,
                       app_ver=app_ver)
    write_plugin_items(xml_tree,
                       plugins_records,
                       api_ver=api_ver,
                       app_id=app,
                       app_ver=app_ver)
    write_gfx_items(xml_tree, gfx_records, api_ver=api_ver, app_id=app)
    write_cert_items(xml_tree,
                     cert_records,
                     api_ver=api_ver,
                     app_id=app,
                     app_ver=app_ver)

    doc = etree.ElementTree(xml_tree)
    request.response.content_type = "application/xml;charset=UTF-8"

    request.response.write(
        etree.tostring(doc,
                       pretty_print=True,
                       xml_declaration=True,
                       encoding='UTF-8').decode('utf-8'))

    return request.response