fix(urlpublisher): raise a 404 when a URL rewrite with rql has no rset

fix #199

authorElodie Thieblin <ethieblin@logilab.fr>
changesetcaa6a76fcc94
branchdefault
phasedraft
hiddenno
parent revision#28bdd6431c69 refactor(gitlab-ci): rename lint_doc to build_doc
child revision<not specified>
files modified by this revision
cubicweb/web/application.py
cubicweb/web/test/unittest_application.py
cubicweb/web/views/basecontrollers.py
cubicweb/web/views/urlpublishing.py
# HG changeset patch
# User Elodie Thieblin <ethieblin@logilab.fr>
# Date 1611659491 -3600
# Tue Jan 26 12:11:31 2021 +0100
# Node ID caa6a76fcc94a369b43055a4a7e40aa6a48e4f82
# Parent 28bdd6431c6924b143cf8c1ee3e059f19fac513c
fix(urlpublisher): raise a 404 when a URL rewrite with rql has no rset

fix #199

diff --git a/cubicweb/web/application.py b/cubicweb/web/application.py
@@ -338,17 +338,21 @@
1          tstart = process_time()
2          commited = False
3          try:
4              # standard processing of the request
5              try:
6 +                # The process function update the req according rewrites rules.
7 +                # It stores in req._rql_rewritten if the rql comes from the endUser.
8                  ctrlid, rset = self.url_resolver.process(req, path)
9                  try:
10                      controller = self.vreg['controllers'].select(ctrlid, req,
11                                                                   appli=self)
12                  except NoSelectableObject:
13                      raise Unauthorized(req._('not authorized'))
14                  req.update_search_state()
15 +                # In case of rewritten rql, controller.publish directly raises
16 +                # notFound if the rset is empty.
17                  result = controller.publish(rset=rset)
18              except Redirect as ex:
19                  # Redirect may be raised by edit controller when everything went
20                  # fine, so attempt to commit
21                  result = self.redirect_handler(req, ex)
diff --git a/cubicweb/web/test/unittest_application.py b/cubicweb/web/test/unittest_application.py
@@ -756,8 +756,22 @@
22          with self.admin_access.web_request() as req:
23              # expect a rset with None in [0][0]
24              req.form['rql'] = 'rql:Any OV1, X WHERE X custom_workflow OV1?'
25              self.app_handle_request(req)
26 
27 +    def test_notfound_raised_on_empty_underlying_rset(self):
28 +        """tests URLs that are rewritten with an underlying rqlquery.
29 +        If the rql query is empty, a NotFound error should be raised."""
30 +
31 +        with self.new_access('anon').web_request('schema/CWUser') as req:
32 +            self.app_handle_request(req)
33 +            self.assertEqual(req.status_out, 200)
34 +
35 +        # There is an underlying rql to fetch the schema of cwetype.
36 +        # As there is no entity «pouet», it should raise NotFound.
37 +        with self.new_access('anon').web_request('schema/pouet') as req:
38 +            self.app_handle_request(req)
39 +            self.assertEqual(req.status_out, 404)
40 +
41 
42  if __name__ == '__main__':
43      unittest_main()
diff --git a/cubicweb/web/views/basecontrollers.py b/cubicweb/web/views/basecontrollers.py
@@ -25,11 +25,11 @@
44                        AuthenticationError, UndoTransactionException,
45                        Forbidden, rdf)
46  from cubicweb.utils import json_dumps
47  from cubicweb.predicates import (authenticated_user, anonymous_user,
48                                  match_form_params)
49 -from cubicweb.web import Redirect
50 +from cubicweb.web import Redirect, NotFound
51  from cubicweb.web.controller import Controller
52  from cubicweb.web.views import vid_from_rset
53  from cubicweb._exceptions import NotAnEntity
54 
55 
@@ -82,12 +82,17 @@
56      """
57      __regid__ = 'view'
58      template = 'main-template'
59 
60      def publish(self, rset=None):
61 -        """publish a request, returning an encoded string"""
62 +        """publish a request, returning an encoded string.
63 +
64 +        :raise NotFound: if the rql has been rewritten (rewrite rules) and the
65 +        resulting rset is empty"""
66          view, rset = self._select_view_and_rset(rset)
67 +        if not rset and getattr(self._cw, '_rql_rewritten', False):
68 +            raise NotFound(self._cw._url)
69          view.set_http_cache_headers()
70          if self._cw.is_client_cache_valid():
71              return b''
72          template = self.appli.main_template_id(self._cw)
73          return self._cw.vreg['views'].main_template(self._cw, template,
diff --git a/cubicweb/web/views/urlpublishing.py b/cubicweb/web/views/urlpublishing.py
@@ -128,11 +128,12 @@
74          elif language_mode in ('http-negotiation', 'url-prefix'):
75              # negotiated language
76              lang = req.negotiated_language()
77              if lang:
78                  req.set_language(lang)
79 -        if req.form.get('rql'):
80 +        origin_rql = req.form.get('rql')
81 +        if origin_rql:
82              if parts[0] in self.vreg['controllers']:
83                  return parts[0], None
84              return 'view', None
85          for evaluator in self.evaluators:
86              try:
@@ -140,10 +141,17 @@
87                  break
88              except PathDontMatch:
89                  continue
90          else:
91              raise NotFound(path)
92 +
93 +        # Store if the final rql (changed by evaluator.evaluate_path) comes from
94 +        # the end user (?rql=Any X Where is CWUser) or comes from a rewrite rule
95 +        # such as SimpleReqRewriter.
96 +        new_rql = req.form.get("rql")
97 +        req._rql_rewritten = origin_rql != new_rql
98 +
99          if pmid is None:
100              pmid = self.default_method
101          return pmid, rset
102 
103