Skip to content

Commit be246f8

Browse files
committed
Fail gracefully on API errors
1 parent 610644e commit be246f8

File tree

3 files changed

+24
-5
lines changed

3 files changed

+24
-5
lines changed

pwned/app_settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
DEFAULTS = {
55
'ENDPOINT': 'https://api.pwnedpasswords.com/range/',
6-
'TIMEOUT': 1, # The default is conservative, it should be <20ms typically, 500ms if uncached
6+
'TIMEOUT': 2, # The default is conservative, it should be <20ms typically, 500ms if uncached
77
'PREFIX_LENGTH': 5,
88
'OCCURRENCE_THRESHOLD': 1, # How many occurrences is too many
99
'USER-AGENT': 'github.com/craigloftus/django-pwned-validator',

pwned/client.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
from hashlib import sha1
2+
import logging
23
import requests
34

45
from . import app_settings
56

67

8+
logger = logging.getLogger(__name__)
9+
10+
711
session = requests.Session()
812
session.headers.update({'User-Agent': app_settings.PWNED['USER-AGENT']})
913

1014

1115
class PwnedClient:
1216

1317
def fetch_range(self, prefix):
14-
# TODO Check for and log errors
15-
url = ''.join([app_settings.PWNED['ENDPOINT'], prefix])
16-
resp = session.get(url, timeout=app_settings.PWNED['TIMEOUT'])
17-
return resp.text
18+
try:
19+
url = ''.join([app_settings.PWNED['ENDPOINT'], prefix])
20+
resp = session.get(url, timeout=app_settings.PWNED['TIMEOUT'])
21+
return resp.text
22+
except requests.exceptions.RequestException as e:
23+
logger.exception("Request to Pwned Password API failed. Validation skipped.")
24+
return ""
1825

1926
def parse_range(self, raw_range):
2027
"""

tests/test_validators.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pytest
22

33
from django.core.exceptions import ValidationError
4+
from requests.exceptions import Timeout
45

56
from pwned.validators import PwnedValidator
67

@@ -16,3 +17,14 @@ def test_validator_found():
1617
def test_validator_not_found():
1718
validator = PwnedValidator()
1819
validator.validate('8CEF1E00B20F463C1E48B589B03660D4E3B9EF7A')
20+
21+
22+
def test_validator_api_unreachable(caplog, monkeypatch):
23+
def raiser(*args, **kwargs):
24+
raise Timeout()
25+
monkeypatch.setattr('requests.sessions.Session.get', raiser)
26+
validator = PwnedValidator()
27+
# Shouldn't throw a ValidationError
28+
validator.validate('password')
29+
for record in caplog.records:
30+
assert record.levelname == 'ERROR'

0 commit comments

Comments
 (0)