def _test_configurable_headers(self, method): # Reserved headers not allowed. content_type = 'not-allowed' self.env.config.set('http-headers', 'Content-Type', content_type) # Control code not allowed. custom1 = '\x00custom1' self.env.config.set('http-headers', 'X-Custom-1', custom1) # Many special characters allowed in header name. custom2 = 'Custom2-!#$%&\'*+.^_`|~' self.env.config.set('http-headers', custom2, 'custom2') # Some special characters not allowed in header name. self.env.config.set('http-headers', 'X-Custom-(3)', 'custom3') req = MockRequest(self.env, method='POST') request_dispatcher = RequestDispatcher(self.env) request_dispatcher.set_default_callbacks(req) self.assertRaises(RequestDone, method, req) self.assertNotEqual('not-allowed', req.headers_sent.get('Content-Type')) self.assertNotIn('x-custom-1', req.headers_sent) self.assertIn(custom2.lower(), req.headers_sent) self.assertNotIn('x-custom-(3)', req.headers_sent) self.assertIn( ('WARNING', "[http-headers] invalid headers are ignored: " "u'content-type': u'not-allowed', " "u'x-custom-1': u'\\x00custom1', " "u'x-custom-(3)': u'custom3'"), self.env.log_messages)
def match_request(self, req): if req.path_info.startswith('/projects'): path_info = req.path_info[10:] if path_info: self.log.debug('TracForgeDispatch: Starting WSGI relaunch for %s (%s)', path_info, req.method) project = path_info.split('/', 1)[0] # Check that we aren't trying to recurse (possible link loop) if project == os.path.basename(self.env.path): req.redirect(req.href()) # Assert permissions on the desination environment try: project_env = open_environment(os.path.join(os.path.dirname(self.env.path), project)) except IOError: raise TracError('No such project "%s"'%project) authname = RequestDispatcher(self.env).authenticate(req) project_perm = PermissionCache(project_env, authname) project_perm.assert_permission('PROJECT_VIEW') self.log.debug('TracForgeDispath: Access granted, running relaunch') self.log.debug('TracForgeDispatch: Status of req.args is %r', req.__dict__.get('args', 'NOT FOUND')) #self.log.debug('TracForgeDispatch: wsgi.input contains %s', req.read()) self._send_project(req, path_info) self.log.debug('TracForgeDispatch: Relaunch completed, terminating request') self.log.debug('TracForgeDispatch: Response was %r', req._response) req._tf_print = True raise RequestDone, 'request done'
def dummy_request(env, uname=None): environ = {} setup_testing_defaults(environ) environ.update({ 'REQUEST_METHOD': 'GET', 'SCRIPT_NAME': urlparse(str(env._abs_href())).path, 'trac.base_url': str(env._abs_href()), }) req = Request(environ, lambda *args, **kwds: None) # Intercept redirection req.redirect = lambda *args, **kwds: None # Setup user information if uname is not None: environ['REMOTE_USER'] = req.authname = uname rd = RequestDispatcher(env) chrome = Chrome(env) req.callbacks.update({ 'authname': rd.authenticate, 'chrome': chrome.prepare_request, 'hdf': getattr(rd, '_get_hdf', None), 'lc_time': rd._get_lc_time, 'locale': getattr(rd, '_get_locale', None), 'perm': rd._get_perm, 'session': rd._get_session, 'tz': rd._get_timezone, 'form_token': rd._get_form_token }) return req
def test_get_session_returns_fake_session(self): """Fake session is returned when database is not reachable.""" sid = self._insert_session()[0] request_dispatcher = RequestDispatcher(self.env) def get_session(req): """Simulates an unreachable database.""" _get_connector = DatabaseManager.get_connector def get_connector(self): raise TracError("Database not reachable") DatabaseManager.get_connector = get_connector DatabaseManager(self.env).shutdown() session = request_dispatcher._get_session(req) DatabaseManager.get_connector = _get_connector return session req = MockRequest(self.env, path_info='/test-stub', cookie='trac_session=%s;' % sid) req.callbacks['session'] = get_session self.assertRaises(RequestDone, request_dispatcher.dispatch, req) self.assertIsInstance(req.session, FakeSession) self.assertIsNone(req.session.sid) self.assertNotIn('name', req.session) self.assertNotIn('email', req.session) self.assertFalse(req.session.authenticated) self.assertEqual('200 Ok', req.status_sent[0]) self.assertIn('<h1>Hello World</h1>', req.response_sent.getvalue())
def setUp(self): self.env = EnvironmentStub() self.req = MockRequest(self.env, args={'hdfdump': '1'}) self.env.clear_component_registry() self.request_dispatcher = RequestDispatcher(self.env) perm = self.req.perm self.request_dispatcher._get_perm = lambda req: perm
def test_invalid_default_date_format_raises_exception(self): self.env.config.set('trac', 'default_date_format', u'ĭšo8601') self.assertEqual(u'ĭšo8601', self.env.config.get('trac', 'default_date_format')) self.assertRaises(ConfigurationError, getattr, RequestDispatcher(self.env), 'default_date_format')
def test_no_request_filters_request_handler_returns_method_false(self): """IRequestHandler doesn't return `method` and no IRequestFilters are registered. The `method` is set to `None`. """ args = ('template.html', {}, 'text/html') request_dispatcher = RequestDispatcher(self.env) resp = request_dispatcher._post_process_request(self.req, *args) self.assertEqual(0, len(request_dispatcher.filters)) self.assertEqual(4, len(resp)) self.assertEqual(args + (None, ), resp) # TODO (1.5.1) remove old API (genshi style) args = ('template.html', {}, {'content_type': 'text/html'}) request_dispatcher = RequestDispatcher(self.env) resp = request_dispatcher._post_process_request(self.req, *args) self.assertEqual(0, len(request_dispatcher.filters)) self.assertEqual(4, len(resp)) self.assertEqual(args + (None, ), resp)
def setUp(self): self.env = EnvironmentStub(disable=['trac.web.auth.LoginModule']) self.request_dispatcher = RequestDispatcher(self.env) self.req = Mock(chrome={'warnings': []}) # Make sure we have no external components hanging around in the # component registry self.old_registry = ComponentMeta._registry ComponentMeta._registry = {}
def test_post_process_request_with_2_args(self): request_dispatcher = RequestDispatcher(self.env) args = ('template.html', {}) resp = request_dispatcher._post_process_request(self.req, *args) self.assertEqual(2, len(request_dispatcher.filters)) self.assertEqual(3, len(resp)) self.assertEqual(2, len(resp[2])) self.assertEqual('en_US', resp[2]['domain']) self.assertTrue(resp[2]['text'])
def setUp(self): self.env = EnvironmentStub() self.req = Mock() self.request_dispatcher = RequestDispatcher(self.env) self.compmgr = ComponentManager() # Make sure we have no external components hanging around in the # component registry self.old_registry = ComponentMeta._registry ComponentMeta._registry = {}
def test_post_process_request_error_handling(self): """The post_process_request methods are called with a triple of `None` values when an exception is raised in process_request or post_process_request, or an empty response is returned by process_request. """ request_dispatcher = RequestDispatcher(self.env) args = (None, ) * 3 resp = request_dispatcher._post_process_request(self.req, *args) self.assertEqual(2, len(request_dispatcher.filters)) self.assertEqual((None, None, None), resp)
def test_5arg_post_process_request_request_handler_returns_method_false(self): """IRequestHandler doesn't return `method` and IRequestFilter accepts `method` as an argument. The `method` is set to `None`. """ self.env.enable_component(self.request_filter['5Arg']) request_dispatcher = RequestDispatcher(self.env) args = ('template.html', {}, 'text/html') resp = request_dispatcher._post_process_request(self.req, *args) self.assertEqual(1, len(request_dispatcher.filters)) self.assertEqual(4, len(resp)) self.assertEqual(args[:3] + (None,), resp)
def simulate_request(self, req): process_request = lambda: RequestDispatcher(self.env).dispatch(req) assert_raises(RequestDone, process_request) response = req.captured_response # Request.send_file will just set _response without using write # - I can't add a property to the Request object dynamically # *per-instance* (in Python you can only add these *per class*) # - Dynamically adding '__setattr__' does also not work per instance if req._response is not None: response.body.write(req._response.read()) response.body.seek(0) return response
def test_resource_not_found_raises_http_not_found(self): """ResourceNotFound error in process_request is trapped and an HTTPNotFound error is raised. """ req = MockRequest(self.env) req.exc_class = ResourceNotFound try: RequestDispatcher(self.env).dispatch(req) except HTTPNotFound, e: self.assertEqual("404 Trac Error (Raised in process_request)", unicode(e))
def test_trac_error_raises_http_internal_error(self): """TracError in process_request is trapped and an HTTPInternalError is raised. """ req = MockRequest(self.env) req.exc_class = TracError try: RequestDispatcher(self.env).dispatch(req) except HTTPInternalError, e: self.assertEqual("500 Trac Error (Raised in process_request)", unicode(e))
def _test_file_not_sent_using_xsendfile_header(self, xsendfile_header): req = MockRequest(self.env) request_dispatcher = RequestDispatcher(self.env) request_dispatcher.set_default_callbacks(req) # File is not sent using xsendfile. self.assertRaises(RequestDone, req.send_file, self.filename) self.assertEqual(['200 Ok'], req.status_sent) self.assertEqual('text/plain', req.headers_sent['Content-Type']) self.assertNotIn(xsendfile_header, req.headers_sent) self.assertEqual('_FileWrapper', type(req._response).__name__) self.assertEqual('', req.response_sent.getvalue())
def test_not_implemented_error_raises_http_internal_server_error(self): """NotImplementedError in process_request is trapped and an HTTPInternalError is raised. """ req = MockRequest(self.env) req.exc_class = NotImplementedError try: RequestDispatcher(self.env).dispatch(req) except HTTPInternalError, e: self.assertEqual( "500 Not Implemented Error (Raised in " "process_request)", unicode(e))
def test_trac_error_raises_http_internal_server_error(self): """TracError in pre_process_request is trapped and an HTTPInternalServerError is raised. """ req = MockRequest(self.env) try: RequestDispatcher(self.env).dispatch(req) except HTTPInternalServerError as e: self.assertEqual("500 Trac Error (Raised in pre_process_request)", unicode(e)) else: self.fail("HTTPInternalServerError not raised")
def setUp(self): self.env = EnvironmentStub() self.request_dispatcher = RequestDispatcher(self.env) self.req = Mock(chrome={'warnings': []}, method='GET', perm=MockPerm(), args={'hdfdump': '1'}, session={}, callbacks={}, send=self._req_send) self.content = None self.content_type = None self.env.clear_component_registry()
def match_request(self, req): if req.path_info.startswith('/projects/'): path_info = req.path_info[10:].lstrip('/') if path_info: self.log.debug( 'TracForgeDispatch: Starting WSGI relaunch for %s (%s)', path_info, req.method) self.log.debug('SN = %s PI = %s', req.environ['SCRIPT_NAME'], req.environ['PATH_INFO']) project_name = path_info.split('/', 1)[0] # Check that we aren't trying to recurse (possible link loop) if project_name == os.path.basename(self.env.path): req.redirect(req.href()) project = Project(self.env, project_name) # Assert permissions on the desination environment if not project.exists: raise TracError('No such project "%s"', project.name) if not project.valid: raise TracError('Project %s is invalid:\n%s', project.name, project.env.exc) # Check that we have permissions in the desired project authname = RequestDispatcher(self.env).authenticate(req) project_perm = PermissionCache(project.env, authname) project_perm.require('PROJECT_LIST') start_response = req._start_response environ = copy.copy(req.environ) # Setup the environment variables environ['SCRIPT_NAME'] = req.href.projects(project.name) environ['PATH_INFO'] = path_info[len(project.name):] environ['trac.env_path'] = project.env_path if 'TRAC_ENV' in environ: del environ['TRAC_ENV'] if 'TRAC_ENV_PARENT_DIR' in environ: del environ['TRAC_ENV_PARENT_DIR'] if 'trac.env_parent' in environ: del environ['trac.env_parent_dir'] environ['tracforge_master_link'] = req.href.projects() # Remove mod_python options to avoid conflicts if 'mod_python.subprocess_env' in environ: del environ['mod_python.subprocess_env'] if 'mod_python.options' in environ: del environ['mod_python.options'] req._response = dispatch_request(environ, start_response) raise RequestDone
def test_permission_error_raises_http_forbidden(self): """TracError in process_request is trapped and an HTTPForbidden error is raised. """ req = MockRequest(self.env) req.exc_class = PermissionError try: RequestDispatcher(self.env).dispatch(req) except HTTPForbidden, e: self.assertEqual( "403 Forbidden (Raised in process_request " "privileges are required to perform this operation. You " "don't have the required permissions.)", unicode(e))
def test_invalid_session_id_returns_fake_session(self): """Fake session is returned when session id is invalid.""" sid = 'a' * 23 + '$' # last char invalid, sid must be alphanumeric. req = MockRequest(self.env, path_info='/test-stub', cookie='trac_session=%s;' % sid) request_dispatcher = RequestDispatcher(self.env) request_dispatcher.set_default_callbacks(req) self.assertRaises(RequestDone, request_dispatcher.dispatch, req) self.assertIsInstance(req.session, FakeSession) self.assertIsNone(req.session.sid) self.assertEqual('200 Ok', req.status_sent[0]) self.assertIn('<h1>Hello World</h1>', req.response_sent.getvalue())
def test_tag_query_save(self): """Save timeline tag query string in session.""" self.assertEqual('tag_query', self.tef.key) from trac.timeline.web_ui import TimelineModule TimelineModule(self.env) perms = PermissionSystem(self.env) perms.grant_permission('anonymous', 'TAGS_VIEW') perms.grant_permission('anonymous', 'TIMELINE_VIEW') req = MockRequest(self.env, path_info='/timeline', args={'tag_query': 'query_str'}) dispatcher = RequestDispatcher(self.env) self.assertRaises(RequestDone, dispatcher.dispatch, req) self.assertEqual('query_str', req.session['timeline.tag_query'])
def test_5arg_post_process_request_request_handler_modifies_method(self): """IRequestFilter modifies `method` returned by IRequestHandler. """ self.env.enable_component(self.request_filter['5ArgXml']) args = ('template.html', {}, 'text/html', 'xhtml') request_dispatcher = RequestDispatcher(self.env) resp = request_dispatcher._post_process_request(self.req, *args) self.assertEqual(1, len(request_dispatcher.filters)) self.assertEqual(4, len(resp)) self.assertEqual(args[:3] + ('xml', ), resp) # TODO (1.5.1) remove old API (genshi style) args = ('template.html', {}, {'content_type': 'text/html'}, 'xhtml') resp = request_dispatcher._post_process_request(self.req, *args) self.assertEqual(1, len(request_dispatcher.filters)) self.assertEqual(4, len(resp)) self.assertEqual(args[:3] + ('xml', ), resp)
def test_get_session_returns_session(self): """Session is returned when database is reachable.""" sid, name, email = self._insert_session() req = MockRequest(self.env, path_info='/test-stub', cookie='trac_session=%s;' % sid) request_dispatcher = RequestDispatcher(self.env) self.assertRaises(RequestDone, request_dispatcher.dispatch, req) self.assertIsInstance(req.session, Session) self.assertEqual(sid, req.session.sid) self.assertEqual(name, req.session['name']) self.assertEqual(email, req.session['email']) self.assertFalse(req.session.authenticated) self.assertEqual('200 Ok', req.status_sent[0]) self.assertIn('<h1>Hello World</h1>', req.response_sent.getvalue())
def test_error_with_lazy_translation(self): self._create_env() os.chmod(self.db_path, 0444) env = Environment(self.env_path) chrome = Chrome(env) dispatcher = RequestDispatcher(env) req = self._create_req(cookie='trac_auth=1234567890') req.callbacks.update({'authname': dispatcher.authenticate, 'chrome': chrome.prepare_request, 'session': dispatcher._get_session, 'locale': dispatcher._get_locale}) translation.make_activable(lambda: req.locale, env.path) try: self._db_query(env) self.fail('ConfigurationError not raised') except ConfigurationError, e: self.assertIn('requires read _and_ write permissions', unicode(e))
def test_4arg_post_process_request_request_handler_returns_method_false(self): """IRequestHandler doesn't return `method` and IRequestFilter doesn't accept `method` as an argument. The `method` is set to `None`. """ self.env.enable_component(self.request_filter['4Arg']) request_dispatcher = RequestDispatcher(self.env) args = ('template.html', {}, 'text/html') resp = request_dispatcher._post_process_request(self.req, *args) self.assertEqual(1, len(request_dispatcher.filters)) self.assertEqual(4, len(resp)) self.assertEqual(args + (None,), resp) # TODO (1.5.1) remove old API (genshi style) args = ('template.html', {}, {'content_type': 'text/html'}) resp = request_dispatcher._post_process_request(self.req, *args) self.assertEqual(1, len(request_dispatcher.filters)) self.assertEqual(4, len(resp)) self.assertEqual(args + (None,), resp)
def test_set_valid_xsendfile_header(self): """Send file using xsendfile header.""" self.env.config.set('trac', 'use_xsendfile', True) self.env.config.set('trac', 'xsendfile_header', 'X-Accel-Redirect') req = MockRequest(self.env) request_dispatcher = RequestDispatcher(self.env) request_dispatcher.set_default_callbacks(req) # File is sent using xsendfile. self.assertRaises(RequestDone, req.send_file, self.filename) self.assertEqual(['200 Ok'], req.status_sent) self.assertEqual('text/plain', req.headers_sent['Content-Type']) self.assertEqual(self.filename, req.headers_sent['X-Accel-Redirect']) self.assertNotIn('X-Sendfile', req.headers_sent) self.assertIsNone(req._response) self.assertEqual('', req.response_sent.getvalue())
def test_5arg_post_process_request_request_handler_returns_method_true(self): """IRequestHandler returns `method` and IRequestFilter accepts the argument. The `method` argument is passed through IRequestFilter implementations. """ self.env.enable_component(self.request_filter['5Arg']) request_dispatcher = RequestDispatcher(self.env) args = ('template.html', {}, 'text/html', 'xhtml') resp = request_dispatcher._post_process_request(self.req, *args) self.assertEqual(1, len(request_dispatcher.filters)) self.assertEqual(4, len(resp)) self.assertEqual(args, resp) # TODO (1.5.1) remove old API (genshi style) args = ('template.html', {}, {'content_type': 'text/html'}, 'xhtml') resp = request_dispatcher._post_process_request(self.req, *args) self.assertEqual(1, len(request_dispatcher.filters)) self.assertEqual(4, len(resp)) self.assertEqual(args, resp)
def dispatchers(self): if not hasattr(self, '_dispatchers'): dispatchers = {} base_path, project = os.path.split(self.env.path) projects = [i for i in os.listdir(base_path) if i != project] for project in projects: path = os.path.join(base_path, project) try: env = open_environment(path) rd = RequestDispatcher(env) except: continue dispatchers[project] = rd self._dispatchers = dispatchers return self._dispatchers