Time out requests to the OTP server

None of the calls to the OTP server set a timeout, so a hanging OTP
server blocked a web worker indefinitely. Each call now times out after
OTP_TIMEOUT seconds (default 10).
This commit is contained in:
sajeyks 2026-11-06 15:26:48 +03:00
commit 122bc387d8
4 changed files with 20 additions and 3 deletions

View file

@ -200,6 +200,7 @@ OTP_ADMIN_NAME = config('ADMIN_NAME')
OTP_ADMIN_REALM = config('ADMIN_REALM')
OTP_USER_REALM = config('USER_REALM')
OTP_ALLOWED_REALMS = config('ALLOWED_REALMS', cast=Csv())
OTP_TIMEOUT = config('OTP_TIMEOUT', default=10, cast=int) # seconds
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (

View file

@ -432,6 +432,17 @@ class OtpSeedTests(LdapTestCase):
self.assertNotIn("cust-pass-123", out.getvalue())
self.assertNotIn("cust-pass-123", "\n".join(logs.output))
def test_otp_requests_have_a_timeout(self):
uid = self.make_ldap_user(password="cust-pass-123")
self.login(uid, "cust-pass-123")
self.client.get("/seeds/")
self.client.post("/otp/", {"username": uid, "password": "cust-pass-123",
"realm": "ungleich-auth"})
calls = self.otp_get.call_args_list + self.otp_post.call_args_list
self.assertTrue(calls)
for call in calls:
self.assertEqual(call.kwargs.get("timeout"), 10)
def test_api_rejects_bad_credentials(self):
uid = self.make_ldap_user(password="cust-pass-123")
resp = self.client.post("/otp/", {"username": uid, "password": "x", "realm": "ungleich-auth"})

View file

@ -546,7 +546,8 @@ class ActivateAccount(View):
'auth_realm': settings.OTP_ADMIN_REALM,
'name': pending.username,
'realm': settings.OTP_USER_REALM
}), headers={'Content-Type': 'application/json'})
}), headers={'Content-Type': 'application/json'},
timeout=settings.OTP_TIMEOUT)
if req.status_code != 201:
logger.error("User {} failed to create its otp seed".format(pending.username))
@ -633,7 +634,8 @@ class SeedRetrieveCreate(APIView):
{
'auth_token': TOTP(settings.OTP_ADMIN_SEED).now(),
'auth_name': settings.OTP_ADMIN_NAME,
'auth_realm': settings.OTP_ADMIN_REALM}), headers={'Content-Type': 'application/json'})
'auth_realm': settings.OTP_ADMIN_REALM}), headers={'Content-Type': 'application/json'},
timeout=settings.OTP_TIMEOUT)
response_data = json.loads(req.text)
@ -652,7 +654,8 @@ class SeedRetrieveCreate(APIView):
'auth_realm': settings.OTP_ADMIN_REALM,
'name': username,
'realm': realm
}), headers={'Content-Type': 'application/json'})
}), headers={'Content-Type': 'application/json'},
timeout=settings.OTP_TIMEOUT)
if req.status_code == 201:
msg = json.loads(req.text)
return Response(msg, 201)
@ -669,6 +672,7 @@ class Seeds(LoginRequiredMixin, View):
seedlist = []
response = requests.get(
settings.OTP_SERVER,
timeout=settings.OTP_TIMEOUT,
headers={'Content-Type': 'application/json'},
data=json.dumps(
{'auth_name': settings.OTP_ADMIN_NAME, 'auth_realm': settings.OTP_ADMIN_REALM, 'auth_token': TOTP(settings.OTP_ADMIN_SEED).now()}))

View file

@ -46,3 +46,4 @@ ADMIN_REALM=""
ADMIN_SEED="" # [secret] base32 TOTP seed of ADMIN_NAME
USER_REALM="" # realm for seeds of new users
ALLOWED_REALMS="" # comma separated realms users may request
#OTP_TIMEOUT=10 # seconds per request to the OTP server