def test_not_compromised(self): """ Non-compromised passwords are detected correctly. """ request_mock = self._get_mock( response_text="{}:5".format(self.sample_password_suffix.replace("A", "3")) ) with mock.patch("requests.get", request_mock): result = api.pwned_password(self.sample_password) request_mock.assert_called_with( url=api.API_ENDPOINT.format(self.sample_password_prefix), headers=self.user_agent, timeout=api.REQUEST_TIMEOUT, ) self.assertEqual(0, result) # The real API doesn't return a result with a zero count, but # test it just in case. request_mock = self._get_mock( response_text="{}:0".format(self.sample_password_suffix) ) with mock.patch("requests.get", request_mock): result = api.pwned_password(self.sample_password) request_mock.assert_called_with( url=api.API_ENDPOINT.format(self.sample_password_prefix), headers=self.user_agent, timeout=api.REQUEST_TIMEOUT, ) self.assertEqual(0, result)
def test_timeout_override(self): """ The custom request timeout setting is honored. """ request_mock = self._get_mock() with mock.patch("requests.get", request_mock): api.pwned_password(self.sample_password) request_mock.assert_called_with( url=api.API_ENDPOINT.format(self.sample_password_prefix), headers=self.user_agent, timeout=0.5, )
def _form_valid_perform_login(self, form): """ Performs login with a correct username/password. Returns if this password has been seen in data breaches, plus the appropriate HTTP response for form_valid. As a side effect, this populates `self.request.user` """ correct_password = form.cleaned_data['password'] if settings.DEBUG and correct_password in settings.WHITELISTED_BAD_PASSWORDS: times_seen = 0 else: times_seen = pwned_password(correct_password) # type: Optional[int] if times_seen: change_password_url = reverse('account_change_password') # Make sure we preserve the original redirect, if there was one next_url = self.request.GET.get('next') if next_url: change_password_url += '?' + urlencode({'next': next_url}) response = form.login(self.request, redirect_url=change_password_url) else: response = super().form_valid(form) return times_seen, response
def test_http_error(self): """ Non-200 HTTP responses from the API are handled gracefully. """ request_mock = self._get_exception_mock(requests.HTTPError()) with mock.patch.object(requests.Response, "raise_for_status", request_mock): result = api.pwned_password(self.sample_password) self.assertEqual(None, result)
def test_timeout(self): """ Connection timeouts to the API are handled gracefully. """ request_mock = self._get_exception_mock(requests.ConnectTimeout()) with mock.patch("requests.get", request_mock): result = api.pwned_password(self.sample_password) self.assertEqual(None, result)
def test_bad_response_no_colon(self): """ Malformed API responses with no colon are handled gracefully. """ request_mock = self._get_mock( response_text=self.sample_password_suffix ) with mock.patch('requests.get', request_mock): result = api.pwned_password(self.sample_password) self.assertEqual(None, result)
def test_bad_text(self): """ Non-numeric counts in API response are handled gracefully. """ request_mock = self._get_mock( response_text='{}:xxx'.format( self.sample_password_suffix ) ) with mock.patch('requests.get', request_mock): result = api.pwned_password(self.sample_password) self.assertEqual(None, result)
def test_bad_response_many_colons(self): """ Malformed API responses with too many colons are gracefully. """ request_mock = self._get_mock( response_text='{}:123:xxx'.format( self.sample_password_suffix ) ) with mock.patch('requests.get', request_mock): result = api.pwned_password(self.sample_password) self.assertEqual(None, result)
def post(self, request, *args, **kwargs): user = self.request.user pk = self.request.POST.get('password_id') check = self.request.POST.get('check_p') password = PasswordEntry.objects.get(pk=pk, owner_password=user) test = password.decrypt_password() if check == "True": count = pwned_password(test) if count is None: return JsonResponse({'password': 0}) else: return JsonResponse({'password': count}) else: return JsonResponse({'password': test})
def test_empty_response(self): """ An empty API response is handled correctly. """ request_mock = self._get_mock(response_text="") with mock.patch("requests.get", request_mock): result = api.pwned_password(self.sample_password) request_mock.assert_called_with( url=api.API_ENDPOINT.format(self.sample_password_prefix), headers=self.user_agent, timeout=api.REQUEST_TIMEOUT, ) self.assertEqual(0, result)
def test_compromised(self): """ Compromised passwords are detected correctly. """ for count in range(1, 10): request_mock = self._get_mock( response_text="{}:{}".format(self.sample_password_suffix, count) ) with mock.patch("requests.get", request_mock): result = api.pwned_password(self.sample_password) request_mock.assert_called_with( url=api.API_ENDPOINT.format(self.sample_password_prefix), headers=self.user_agent, timeout=api.REQUEST_TIMEOUT, ) self.assertEqual(count, result)
def password_list(request): if request.method == 'POST' and request.is_ajax(): user = request.user pk = request.POST.get('password_id') check = request.POST.get('check_p') password = PasswordEntry.objects.get(pk=pk, owner_password=user) test = password.decrypt_password() if check == "True": count = pwned_password(test) if count is None: return JsonResponse({'password': 0}) else: return JsonResponse({'password': count}) else: return JsonResponse({'password': test}) user = request.user passwords = PasswordEntry.objects.filter(owner_password=user) return render(request, "password/password_list.html", {"passwords": passwords})
def test_unicode_requirement(self): with self.assertRaises(TypeError): api.pwned_password(self.sample_password.encode("utf-8"))