def test_admin_import_subscribers_existing(self): """ Test importing already existing subscriptions. """ subscription = make_subscription(self.newsletter, '*****@*****.**') subscription.save() with patch_logger('newsletter.addressimport.parsers', 'warning') as messages: response = self.admin_import_subscribers( 'addresses.csv', ignore_errors='true' ) self.assertContains( response, "1 subscription has been successfully added." ) self.assertEqual(len(messages), 1) self.assertEqual(self.newsletter.subscription_set.count(), 2) with patch_logger('newsletter.addressimport.parsers', 'warning') as messages: response = self.admin_import_file('addresses.csv') self.assertContains( response, "Some entries are already subscribed to." ) self.assertEqual(len(messages), 1) self.assertEqual(self.newsletter.subscription_set.count(), 2)
def test_logger_warn_view(self): """ Makes sure debug messages are captured """ with patch_logger('log_trigger.views', 'warn') as calls: self.client.get(reverse('log_trigger:logger_warn_view')) # NOQA self.assertEqual(calls[0], 'Warn message')
def test_disallowed_user_agents(self): with patch_logger('django.request', 'warning') as log_messages: request = self.rf.get('/slash') request.META['HTTP_USER_AGENT'] = 'foo' r = CommonMiddleware().process_request(request) self.assertEqual(r.status_code, 403) self.assertEqual(log_messages, ['Forbidden (User agent): /slash'])
def test_logger_critical_view(self): """ Makes sure critical messages are captured """ with patch_logger('log_trigger.views', 'critical') as calls: self.client.get(reverse('log_trigger:logger_critical_view')) # NOQA self.assertEqual(calls[0], 'Critical message')
def test_decode_failure_logged_to_security(self): bad_encode = base64.b64encode(b'flaskdj:alkdjf') with patch_logger('django.security.SuspiciousSession', 'warning') as calls: self.assertEqual({}, self.session.decode(bad_encode)) # check that the failed decode is logged self.assertEqual(len(calls), 1) self.assertTrue('corrupted' in calls[0])
def test_decode_failure_logged_to_security(self): bad_encode = base64.b64encode(b"flaskdj:alkdjf") with patch_logger("django.security.SuspiciousSession", "warning") as calls: self.assertEqual({}, self.session.decode(bad_encode)) # check that the failed decode is logged self.assertEqual(len(calls), 1) self.assertIn("corrupted", calls[0])
def test_log_custom_message(self): with patch_logger("django.request", "debug") as calls: self.client.get("/middleware_exceptions/view/") self.assertEqual(len(calls), 1) self.assertEqual( calls[0], "MiddlewareNotUsed('middleware_exceptions.tests.MyMiddlewareWithExceptionMessage'): spam eggs" )
def test_disallowed_user_agents(self): with patch_logger("django.request", "warning") as log_messages: request = self.rf.get("/slash") request.META["HTTP_USER_AGENT"] = "foo" r = CommonMiddleware().process_request(request) self.assertEqual(r.status_code, 403) self.assertEqual(log_messages, ["Forbidden (User agent): /slash"])
def test_unsubscribe_request_post_error(self): """ Test whether a failing unsubscribe request email generated an error in the context. We do this by overriding the default mail backend to a settings which we know pretty sure is bound to fail. """ subscription = Subscription(newsletter=self.n, name=self.testname, email=self.testemail, subscribed=True) subscription.save() with override_settings( EMAIL_BACKEND='tests.utils.FailingEmailBackend' ): with patch_logger('newsletter.views', 'error') as messages: response = self.client.post( self.unsubscribe_url, {'email_field': self.testemail} ) self.assertEqual(len(messages), 1) self.assertIn("Connection refused", messages[0]) self.assertTrue(response.context['error'])
def test_logger_error_view(self): """ Makes sure error messages are captured """ with patch_logger('log_trigger.views', 'error') as calls: self.client.get(reverse('log_trigger:logger_error_view')) # NOQA self.assertEqual(calls[0], 'Error message')
def test_formregistry_ready_dict(): fr = FormRegistry(name='default') newconfig = { 'HELLO': ['test_app.forms.DatetimeForm', {'blip': 'blop'}], 'HELLO2': { 'int': 1, 'email': '*****@*****.**', 'url': 'https://news.bbc.co.uk/', }, } with override_settings(STAGESETTINGS=newconfig): with patch_logger('stagesetting.utils', 'info') as logger_calls: result = fr.ready(sender=None, instance=None, model=RuntimeSetting) # implicit = RuntimeSetting.objects.get(key='HELLO2') # assert fr.deserialize(implicit.raw_value) == { # 'email': '*****@*****.**', # 'int': 1, # 'url': 'https://news.bbc.co.uk/' # } assert fr._get_default(key='HELLO2') == { "email": "*****@*****.**", "int": 1, "url": "https://news.bbc.co.uk/" } # assert len(result) == 2 assert logger_calls == ['HELLO2 config is a dictionary, assuming it ' 'represents both the form and default values']
def test_disallowed_host_exception_trigger(self): """ Makes sure disallowed host gets captured """ with patch_logger('django.security.DisallowedHost', 'error') as calls: self.client.get(reverse('log_trigger:disallowed_host_exception_view')) # NOQA self.assertEqual(len(calls), 1) self.assertEqual(calls[0], 'DisallowedHost Exception')
def test_log(self): with patch_logger('django.request', 'debug') as calls: self.client.get('/middleware_exceptions/view/') self.assertEqual(len(calls), 1) self.assertEqual( calls[0], "MiddlewareNotUsed: 'middleware_exceptions.tests.MyMiddleware'" )
def test_put_and_delete_rejected(self): """ Tests that HTTP PUT and DELETE methods have protection """ req = TestingHttpRequest() req.method = 'PUT' with patch_logger('django.security.csrf', 'warning') as logger_calls: req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) self.assertEqual(403, req2.status_code) self.assertEqual(logger_calls[0], 'Forbidden (%s): ' % REASON_NO_CSRF_COOKIE) req = TestingHttpRequest() req.method = 'DELETE' with patch_logger('django.security.csrf', 'warning') as logger_calls: req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) self.assertEqual(403, req2.status_code) self.assertEqual(logger_calls[0], 'Forbidden (%s): ' % REASON_NO_CSRF_COOKIE)
def test_runtime_settings_with_middleware_doesnt_apply_twice(rf): request = rf.get('/') mw = ApplyRuntimeSettings() mw.process_view(request=request, view_func=None, view_args=(), view_kwargs={}) with patch_logger('stagesetting.middleware', 'warning') as logger_calls: mw.process_view(request=request, view_func=None, view_args=(), view_kwargs={}) message = ['Another middleware already set `request.stagesetting`'] assert logger_calls == message
def test_poisoned_http_host_admin_site(self): "Poisoned HTTP_HOST headers can't be used for reset emails on admin views" with patch_logger('django.security.DisallowedHost', 'error') as logger_calls: response = self.client.post('/admin_password_reset/', {'email': '*****@*****.**'}, HTTP_HOST='www.example:[email protected]' ) self.assertEqual(response.status_code, 400) self.assertEqual(len(mail.outbox), 0) self.assertEqual(len(logger_calls), 1)
def test_process_request_no_csrf_cookie(self): """ Check that if no CSRF cookies is present, the middleware rejects the incoming request. This will stop login CSRF. """ with patch_logger('django.security.csrf', 'warning') as logger_calls: req = self._get_POST_no_csrf_cookie_request() req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) self.assertEqual(403, req2.status_code) self.assertEqual(logger_calls[0], 'Forbidden (%s): ' % REASON_NO_CSRF_COOKIE)
def test_process_request_csrf_cookie_no_token(self): """ Check that if a CSRF cookie is present but no token, the middleware rejects the incoming request. """ with patch_logger('django.security.csrf', 'warning') as logger_calls: req = self._get_POST_csrf_cookie_request() req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) self.assertEqual(403, req2.status_code) self.assertEqual(logger_calls[0], 'Forbidden (%s): ' % REASON_BAD_TOKEN)
def test_changelist_disallows_password_lookups(self): # Make me a superuser before loging in. User.objects.filter(username='******').update(is_staff=True, is_superuser=True) self.login() # A lookup that tries to filter on password isn't OK with patch_logger('django.security.DisallowedModelAdminLookup', 'error') as logger_calls: response = self.client.get('/admin/auth/user/?password__startswith=sha1$') self.assertEqual(response.status_code, 400) self.assertEqual(len(logger_calls), 1)
def test_invalid_update(self): """ Test an invalid update, which should fail. """ # Make sure no subscriptions exist on beforehand Subscription.objects.all().delete() # A post without any form elements should fail, horribly with patch_logger('newsletter.views', 'warning') as messages: self.client.post(self.list_url) self.assertEqual(len(messages), 1) self.assertIn("Invalid form post received", messages[0]) # A post with correct management data with weird values # should cause the formset not to validate. # Construct management form data total_forms = self.newsletters.filter(visible=True).count() params = { 'form-TOTAL_FORMS': total_forms, 'form-INITIAL_FORMS': total_forms } # Add subscribe to all newsletters count = 0 for n in self.newsletters.filter(visible=True): params.update({ # Use a wrong value here 'form-%d-id' % count: 1000, 'form-%d-subscribed' % count: '1' }) count += 1 # Post the form with patch_logger('newsletter.views', 'warning') as messages: self.client.post(self.list_url, params) self.assertEqual(len(messages), 1) self.assertIn("Invalid form post received", messages[0]) # Assert no subscriptions have been created self.assertFalse( Subscription.objects.filter(subscribed=True).exists())
def test_olwidget_empty_string(self): geoadmin = site._registry[City] form = geoadmin.get_changelist_form(None)({'point': ''}) with patch_logger('django.contrib.gis', 'error') as logger_calls: output = str(form['point']) self.assertInHTML( '<textarea id="id_point" class="vWKTField required" cols="150"' ' rows="10" name="point"></textarea>', output ) self.assertEqual(logger_calls, [])
def test_command_option(self): with patch_logger('test', 'info') as logger: call_command( 'shell', command=( 'import django; from logging import getLogger; ' 'getLogger("test").info(django.__version__)' ), ) self.assertEqual(len(logger), 1) self.assertEqual(logger[0], __version__)
def test_message_str(self): m1 = Message(title='Test message', slug='test-message') with patch_logger('newsletter.models', 'warning') as warnings: self.assertEqual(six.text_type(m1), "Test message") self.assertEqual(len(warnings), 1) m2 = Message.objects.create( title='Test message str', newsletter=self.n, slug='test-message-str' ) self.assertEqual(six.text_type(m2), "Test message str in Test newsletter")
def test_extra_args(self): editor = connection.schema_editor(collect_sql=True) sql = 'SELECT * FROM foo WHERE id in (%s, %s)' params = [42, 1337] with patch_logger('django.db.backends.schema', 'debug', log_kwargs=True) as logger: editor.execute(sql, params) self.assertEqual( logger, [( 'SELECT * FROM foo WHERE id in (%s, %s); (params [42, 1337])', {'extra': {'sql': sql, 'params': params}}, )] )
def test_https(self): request = WSGIRequest(RequestFactory().get('/').environ) request.makefile = lambda *args, **kwargs: BytesIO() handler = WSGIRequestHandler(request, '192.168.0.2', None) with patch_logger('django.server', 'error') as messages: handler.log_message("GET %s %s", '\x16\x03', "4") self.assertIn( "You're accessing the development server over HTTPS, " "but it only supports HTTP.", messages[0] )
def test_post_data_read_failure(self): """ #20128 -- IOErrors during POST data reading should be caught and treated as if the POST data wasn't there. """ class CsrfPostRequest(HttpRequest): """ HttpRequest that can raise an IOError when accessing POST data """ def __init__(self, token, raise_error): super().__init__() self.method = 'POST' self.raise_error = False self.COOKIES[settings.CSRF_COOKIE_NAME] = token # Handle both cases here to prevent duplicate code in the # session tests. self.session = {} self.session[CSRF_SESSION_KEY] = token self.POST['csrfmiddlewaretoken'] = token self.raise_error = raise_error def _load_post_and_files(self): raise IOError('error reading input data') def _get_post(self): if self.raise_error: self._load_post_and_files() return self._post def _set_post(self, post): self._post = post POST = property(_get_post, _set_post) token = ('ABC' + self._csrf_id)[:CSRF_TOKEN_LENGTH] req = CsrfPostRequest(token, raise_error=False) self.mw.process_request(req) resp = self.mw.process_view(req, post_form_view, (), {}) self.assertIsNone(resp) req = CsrfPostRequest(token, raise_error=True) with patch_logger('django.security.csrf', 'warning') as logger_calls: self.mw.process_request(req) resp = self.mw.process_view(req, post_form_view, (), {}) self.assertEqual(resp.status_code, 403) self.assertEqual(logger_calls[0], 'Forbidden (%s): ' % REASON_BAD_TOKEN)
def test_admin_import_subscribers_duplicates(self): """ Test importing a file with duplicate addresses. """ with patch_logger('newsletter.addressimport.parsers', 'warning') as messages: response = self.admin_import_subscribers( 'addresses_duplicates.csv', ignore_errors='true' ) self.assertContains( response, "2 subscriptions have been successfully added." ) self.assertEqual(len(messages), 2) self.assertEqual(self.newsletter.subscription_set.count(), 2)
def test_strips_underscore_headers(self): """WSGIRequestHandler ignores headers containing underscores. This follows the lead of nginx and Apache 2.4, and is to avoid ambiguity between dashes and underscores in mapping to WSGI environ, which can have security implications. """ def test_app(environ, start_response): """A WSGI app that just reflects its HTTP environ.""" start_response('200 OK', []) http_environ_items = sorted( '%s:%s' % (k, v) for k, v in environ.items() if k.startswith('HTTP_') ) yield (','.join(http_environ_items)).encode() rfile = BytesIO() rfile.write(b"GET / HTTP/1.0\r\n") rfile.write(b"Some-Header: good\r\n") rfile.write(b"Some_Header: bad\r\n") rfile.write(b"Other_Header: bad\r\n") rfile.seek(0) # WSGIRequestHandler closes the output file; we need to make this a # no-op so we can still read its contents. class UnclosableBytesIO(BytesIO): def close(self): pass wfile = UnclosableBytesIO() def makefile(mode, *a, **kw): if mode == 'rb': return rfile elif mode == 'wb': return wfile request = Stub(makefile=makefile) server = Stub(base_environ={}, get_app=lambda: test_app) # We don't need to check stderr, but we don't want it in test output with patch_logger('django.server', 'info'): # instantiating a handler runs the request as side effect WSGIRequestHandler(request, '192.168.0.2', server) wfile.seek(0) body = list(wfile.readlines())[-1] self.assertEqual(body, b'HTTP_SOME_HEADER:good')
def test_extra_args(self): editor = connection.schema_editor(collect_sql=True) sql = "SELECT * FROM foo WHERE id in (%s, %s)" params = [42, 1337] with patch_logger("django.db.backends.schema", "debug", log_kwargs=True) as logger: editor.execute(sql, params) self.assertEqual( logger, [ ( "SELECT * FROM foo WHERE id in (%s, %s); (params [42, 1337])", {"extra": {"sql": "SELECT * FROM foo WHERE id in (%s, %s)", "params": [42, 1337]}}, ) ], )
def test_olwidget_invalid_string(self): geoadmin = site._registry[City] form = geoadmin.get_changelist_form(None)({'point': 'INVALID()'}) with patch_logger('django.contrib.gis', 'error') as logger_calls: output = str(form['point']) self.assertInHTML( '<textarea id="id_point" class="vWKTField required" cols="150"' ' rows="10" name="point"></textarea>', output ) self.assertEqual(len(logger_calls), 1) self.assertEqual( logger_calls[0], "Error creating geometry from value 'INVALID()' (String input " "unrecognized as WKT EWKT, and HEXEWKB.)" )
def test_post_data_read_failure(self): """ #20128 -- IOErrors during POST data reading should be caught and treated as if the POST data wasn't there. """ class CsrfPostRequest(HttpRequest): """ HttpRequest that can raise an IOError when accessing POST data """ def __init__(self, token, raise_error): super(CsrfPostRequest, self).__init__() self.method = 'POST' self.raise_error = False self.COOKIES[settings.CSRF_COOKIE_NAME] = token self.POST['csrfmiddlewaretoken'] = token self.raise_error = raise_error def _load_post_and_files(self): raise IOError('error reading input data') def _get_post(self): if self.raise_error: self._load_post_and_files() return self._post def _set_post(self, post): self._post = post POST = property(_get_post, _set_post) token = ('ABC' + self._csrf_id)[:CSRF_TOKEN_LENGTH] req = CsrfPostRequest(token, raise_error=False) resp = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) self.assertIsNone(resp) req = CsrfPostRequest(token, raise_error=True) with patch_logger('django.security.csrf', 'warning') as logger_calls: resp = CsrfViewMiddleware().process_view(req, post_form_view, (), {}) self.assertEqual(resp.status_code, 403) self.assertEqual(logger_calls[0], 'Forbidden (%s): ' % REASON_BAD_TOKEN)
def test_field_string_value(self): """ Initialization of a geometry field with a valid/empty/invalid string. Only the invalid string should trigger an error log entry. """ class PointForm(forms.Form): pt1 = forms.PointField(srid=4326) pt2 = forms.PointField(srid=4326) pt3 = forms.PointField(srid=4326) form = PointForm({ 'pt1': 'SRID=4326;POINT(7.3 44)', # valid 'pt2': '', # empty 'pt3': 'PNT(0)', # invalid }) with patch_logger('django.contrib.gis', 'error') as logger_calls: output = str(form) # The first point can't use assertInHTML() due to non-deterministic # ordering of the rendered dictionary. pt1_serialized = re.search(r'<textarea [^>]*>({[^<]+})<', output).groups()[0] pt1_json = pt1_serialized.replace('"', '"') pt1_expected = GEOSGeometry(form.data['pt1']).transform(3857, clone=True) self.assertJSONEqual(pt1_json, pt1_expected.json) self.assertInHTML( '<textarea id="id_pt2" class="vSerializedField required" cols="150"' ' rows="10" name="pt2"></textarea>', output ) self.assertInHTML( '<textarea id="id_pt3" class="vSerializedField required" cols="150"' ' rows="10" name="pt3"></textarea>', output ) # Only the invalid PNT(0) should trigger an error log entry self.assertEqual(len(logger_calls), 1) self.assertEqual( logger_calls[0], "Error creating geometry from value 'PNT(0)' (String input " "unrecognized as WKT EWKT, and HEXEWKB.)" )
def test_subscribe_request_post_error(self): """ Test whether a failing subscribe request email generated an error in the context. We do this by overriding the default mail backend to a settings which we know pretty sure is bound to fail. """ with override_settings( EMAIL_BACKEND='tests.utils.FailingEmailBackend'): with patch_logger('newsletter.views', 'error') as messages: response = self.client.post( self.subscribe_url, { 'name_field': 'Test Name', 'email_field': '*****@*****.**' }) self.assertEqual(len(messages), 1) self.assertIn("Connection refused", messages[0]) self.assertTrue(response.context['error'])
def test_unsubscribe_request_post_error(self): """ Test whether a failing unsubscribe request email generated an error in the context. We do this by overriding the default mail backend to a settings which we know pretty sure is bound to fail. """ subscription = Subscription(newsletter=self.n, name='Test Name', email='*****@*****.**', subscribed=True) subscription.save() with override_settings( EMAIL_BACKEND='tests.utils.FailingEmailBackend'): with patch_logger('newsletter.views', 'error') as messages: response = self.client.post(self.unsubscribe_url, {'email_field': '*****@*****.**'}) self.assertEqual(len(messages), 1) self.assertIn("Connection refused", messages[0]) self.assertTrue(response.context['error'])
def _log_level_code(level, status_code): with patch_logger('django.server', level) as messages: handler.log_message('GET %s %s', 'A', str(status_code)) return messages
def test_suspicious_operation_uses_sublogger(self): with patch_logger('django.security.DisallowedHost', 'error') as calls: self.client.get('/suspicious_spec/') self.assertEqual(len(calls), 1) self.assertEqual(calls[0], 'dubious')
def test_suspicious_operation_creates_log_message(self): with patch_logger('django.security.SuspiciousOperation', 'error') as calls: self.client.get('/suspicious/') self.assertEqual(len(calls), 1) self.assertEqual(calls[0], 'dubious')
def no_request_warning(): """Patch django.request logger to ignore warnings.""" return patch_logger('django.request', 'warning', log_kwargs=False)
def test_do_not_log_when_debug_is_false(self): with patch_logger('django.request', 'debug') as calls: self.client.get('/middleware_exceptions/view/') self.assertEqual(len(calls), 0)
def run_prune_command(*args, **opts): # the command uses logging instead of prints, use patch_logger to retrieve and return the output with patch_logger("ara.api.management.commands.prune", "info") as logs: call_command("prune", *args, **opts) return logs
def test_400(self): # When DEBUG=True, technical_500_template() is called. with patch_logger('django.security.SuspiciousOperation', 'error'): response = self.client.get('/raises400/') self.assertContains(response, '<div class="context" id="', status_code=400)
def test_changelist_disallows_password_lookups(self): # A lookup that tries to filter on password isn't OK with patch_logger('django.security.DisallowedModelAdminLookup', 'error') as logger_calls: response = self.client.get('/admin/auth/user/?password__startswith=sha1$') self.assertEqual(response.status_code, 400) self.assertEqual(len(logger_calls), 1)