Skip to content

Commit 22847c5

Browse files
sethmlarsonhugovk
andauthored
Delineate the guest and API key authentication branches (#2946)
Co-authored-by: Hugo van Kemenade <[email protected]>
1 parent 500173e commit 22847c5

File tree

3 files changed

+29
-14
lines changed

3 files changed

+29
-14
lines changed

apps/downloads/tests/test_views.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,7 @@ def test_invalid_token(self):
194194

195195
url = self.create_url("os")
196196
response = self.client.get(url, headers={"authorization": self.Authorization_invalid})
197-
# TODO: API v1 returns 200 for a GET request even if token is invalid.
198-
# 'StaffAuthorization.read_list` returns 'object_list' unconditionally,
199-
# and 'StaffAuthorization.read_detail` returns 'True'.
200-
self.assertIn(response.status_code, [200, 401])
197+
self.assertEqual(response.status_code, 401)
201198

202199
def test_get_os(self):
203200
response = self.client.get(self.create_url("os"))

pydotorg/resources.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@
1212
class ApiKeyOrGuestAuthentication(ApiKeyAuthentication):
1313
"""Authentication backend that falls back to guest access when no API key is provided."""
1414

15-
def _unauthorized(self):
16-
"""Allow guests anyway."""
17-
# Allow guests anyway
18-
return True
19-
2015
def is_authenticated(self, request, **kwargs):
2116
"""Authenticate via API key, handling custom user model.
2217
@@ -26,19 +21,26 @@ def is_authenticated(self, request, **kwargs):
2621
User = get_user_model() # noqa: N806 - Django convention for user model reference
2722
username_field = User.USERNAME_FIELD
2823

24+
# Note that it's only safe to return 'True'
25+
# in the guest case. If there is an API key supplied
26+
# then we must not return 'True' unless the
27+
# API key is valid.
2928
try:
3029
username, api_key = self.extract_credentials(request)
3130
except ValueError:
32-
return self._unauthorized()
33-
31+
return True # Allow guests.
3432
if not username or not api_key:
35-
return self._unauthorized()
33+
return True # Allow guests.
34+
35+
# IMPORTANT: Beyond this point we are no longer
36+
# handling the guest case, so all incorrect usernames
37+
# or credentials MUST return HttpUnauthorized()
3638

3739
try:
3840
lookup_kwargs = {username_field: username}
3941
user = User.objects.get(**lookup_kwargs)
4042
except (User.DoesNotExist, User.MultipleObjectsReturned):
41-
return self._unauthorized()
43+
return HttpUnauthorized()
4244

4345
if not self.check_active(user):
4446
return False

pydotorg/tests/test_resources.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from django.contrib.auth import get_user_model
22
from django.http import HttpRequest
33
from django.test import TestCase
4+
from tastypie.http import HttpUnauthorized
45

56
from pydotorg.resources import ApiKeyOrGuestAuthentication
67

@@ -21,6 +22,21 @@ def test_authentication(self):
2122
auth = ApiKeyOrGuestAuthentication()
2223
self.assertTrue(auth.is_authenticated(request))
2324

24-
request.GET["username"] = self.staff_user.email
25+
request.GET["username"] = self.staff_user.username
26+
self.assertTrue(auth.is_authenticated(request))
27+
2528
request.GET["api_key"] = self.staff_user.api_key.key
2629
self.assertTrue(auth.is_authenticated(request))
30+
31+
def test_authentication_staff_unauthorized(self):
32+
auth = ApiKeyOrGuestAuthentication()
33+
34+
request = HttpRequest()
35+
request.GET["username"] = self.staff_user.username
36+
request.GET["api_key"] = "not-api-key"
37+
self.assertIsInstance(auth.is_authenticated(request), HttpUnauthorized)
38+
39+
request = HttpRequest()
40+
request.GET["username"] = "not-staff"
41+
request.GET["api_key"] = self.staff_user.api_key.key
42+
self.assertIsInstance(auth.is_authenticated(request), HttpUnauthorized)

0 commit comments

Comments
 (0)