Skip to content

Commit 94dd104

Browse files
committed
Add perm to demote user to anon throttle rate
Signed-off-by: Keshav Priyadarshi <git@keshav.space>
1 parent f3c0437 commit 94dd104

File tree

4 files changed

+40
-8
lines changed

4 files changed

+40
-8
lines changed

vulnerabilities/migrations/0093_alter_apiuser_options.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Generated by Django 4.2.22 on 2025-06-13 08:07
1+
# Generated by Django 4.2.22 on 2025-06-13 12:44
22

33
from django.db import migrations
44

@@ -14,9 +14,10 @@ class Migration(migrations.Migration):
1414
name="apiuser",
1515
options={
1616
"permissions": [
17-
("throttle_unrestricted", "Exempt from API throttling limits"),
18-
("throttle_18000_hour", "Can make 18000 API requests per hour"),
19-
("throttle_14400_hour", "Can make 14400 API requests per hour"),
17+
("throttle_unrestricted", "Can make api requests without throttling limits"),
18+
("throttle_18000_hour", "Can make 18000 api requests per hour"),
19+
("throttle_14400_hour", "Can make 14400 api requests per hour"),
20+
("throttle_3600_hour", "Can make 3600 api requests per hour"),
2021
]
2122
},
2223
),

vulnerabilities/models.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,9 +1497,10 @@ class ApiUser(UserModel):
14971497
class Meta:
14981498
proxy = True
14991499
permissions = [
1500-
("throttle_unrestricted", "Exempt from API throttling limits"),
1501-
("throttle_18000_hour", "Can make 18000 API requests per hour"),
1502-
("throttle_14400_hour", "Can make 14400 API requests per hour"),
1500+
("throttle_unrestricted", "Can make api requests without throttling limits"),
1501+
("throttle_18000_hour", "Can make 18000 api requests per hour"),
1502+
("throttle_14400_hour", "Can make 14400 api requests per hour"),
1503+
("throttle_3600_hour", "Can make 3600 api requests per hour"),
15031504
]
15041505

15051506

vulnerabilities/tests/test_throttling.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,18 @@ def setUp(self):
4141
# See https://www.django-rest-framework.org/api-guide/throttling/#setting-up-the-cache
4242
cache.clear()
4343

44+
permission_3600 = Permission.objects.get(codename="throttle_3600_hour")
4445
permission_14400 = Permission.objects.get(codename="throttle_14400_hour")
4546
permission_18000 = Permission.objects.get(codename="throttle_18000_hour")
4647
permission_unrestricted = Permission.objects.get(codename="throttle_unrestricted")
4748

49+
# user with 3600/hour permission
50+
self.th_3600_user = ApiUser.objects.create_api_user(username="z@mail.com")
51+
self.th_3600_user.user_permissions.add(permission_3600)
52+
self.th_3600_user_auth = f"Token {self.th_3600_user.auth_token.key}"
53+
self.th_3600_user_csrf_client = APIClient(enforce_csrf_checks=True)
54+
self.th_3600_user_csrf_client.credentials(HTTP_AUTHORIZATION=self.th_3600_user_auth)
55+
4856
# basic user without any special throttling perm
4957
self.basic_user = ApiUser.objects.create_api_user(username="a@mail.com")
5058
self.basic_user_auth = f"Token {self.basic_user.auth_token.key}"
@@ -77,6 +85,20 @@ def setUp(self):
7785
self.csrf_client_anon = APIClient(enforce_csrf_checks=True)
7886
self.csrf_client_anon_1 = APIClient(enforce_csrf_checks=True)
7987

88+
def test_user_with_3600_perm_throttling(self):
89+
simulate_throttle_usage(
90+
url="/api/packages",
91+
client=self.th_3600_user_csrf_client,
92+
mock_use_count=3599,
93+
)
94+
95+
response = self.th_3600_user_csrf_client.get("/api/packages")
96+
self.assertEqual(response.status_code, status.HTTP_200_OK)
97+
98+
# exhausted 3600/hr allowed requests.
99+
response = self.th_3600_user_csrf_client.get("/api/packages")
100+
self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS)
101+
80102
def test_basic_user_throttling(self):
81103
simulate_throttle_usage(
82104
url="/api/packages",
@@ -87,7 +109,7 @@ def test_basic_user_throttling(self):
87109
response = self.basic_user_csrf_client.get("/api/packages")
88110
self.assertEqual(response.status_code, status.HTTP_200_OK)
89111

90-
# exhausted 10800/hr allowed requests for basic user.
112+
# exhausted 10800/hr allowed requests.
91113
response = self.basic_user_csrf_client.get("/api/packages")
92114
self.assertEqual(response.status_code, status.HTTP_429_TOO_MANY_REQUESTS)
93115

vulnerabilities/throttling.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313

1414

1515
class PermissionBasedUserRateThrottle(UserRateThrottle):
16+
"""
17+
Throttle authenticated users based on their assigned permissions.
18+
If no throttling permission is assigned, default to rate for `user`
19+
scope provided via `DEFAULT_THROTTLE_RATES` in settings.py.
20+
"""
21+
1622
def allow_request(self, request, view):
1723
user = request.user
1824

@@ -23,6 +29,8 @@ def allow_request(self, request, view):
2329
self.rate = "18000/hour"
2430
elif user.has_perm("vulnerabilities.throttle_14400_hour"):
2531
self.rate = "14400/hour"
32+
elif user.has_perm("vulnerabilities.throttle_3600_hour"):
33+
self.rate = "3600/hour"
2634

2735
self.num_requests, self.duration = self.parse_rate(self.rate)
2836

0 commit comments

Comments
 (0)