Security review: staff admin lockdown, unicode-safe login/phone/grade inputs, demo guest not upgradable, home cache not floodable

- accounts admin: phone/is_guest read-only for non-superusers, device_id filter lookups refused,
  auth.User/Group admin writable only by superusers
- campaigns: delivery log needs view permission
- phone/OTP/birth date: any Unicode digit normalised or rejected with 400; unknown gradeId is 400
- shared demo guest is never turned into a person's account by logging in
- /api/home with unknown gradeId is no longer cached (LocMemCache flood reset throttle counters)
- /api/progress/nodes: one query, huge unit is 400
- BazaarCredential token filter lookups refused

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-05 09:34:42 +03:30
parent fedd10ff22
commit 28b8527760
15 changed files with 422 additions and 41 deletions

View File

@@ -348,12 +348,12 @@ class LoginPhoneRateLimitTests(TestCase):
for _ in range(3):
response = self.client.post(
'/api/auth/login', {'phone': phone, 'otp': 'wrong'}, format='json',
'/api/auth/login', {'phone': phone, 'otp': '000000'}, format='json',
)
self.assertEqual(response.status_code, 400)
blocked_response = self.client.post(
'/api/auth/login', {'phone': phone, 'otp': 'wrong'}, format='json',
'/api/auth/login', {'phone': phone, 'otp': '000000'}, format='json',
)
self.assertEqual(blocked_response.status_code, 429)
@@ -1476,6 +1476,8 @@ class AuthUserRowCacheTests(TestCase):
@override_settings(AUTH_USER_CACHE_TTL=30)
def test_set_grade_still_works_with_cache_on(self):
from content.models import Grade
Grade.objects.create(pk=3, title='g3', icon='', color='')
r = self.client.post(
'/api/user/grade', {'gradeId': 3}, format='json',
HTTP_AUTHORIZATION=f'Bearer {self.token}')