Close /api/health details and API docs to the public

Health: public gets only ok/error; details need X-Internal-Key. Docs/schema: admin session only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 13:05:19 +03:30
parent d6529956e1
commit 22c3196343
9 changed files with 74 additions and 53 deletions

View File

@@ -6,7 +6,7 @@
"""
import json
from django.test import TestCase
from django.test import override_settings, TestCase
from content.models import Grade, Lesson, Node, Question, Unit
from content.question_io import ImportError_, export_questions, import_questions, parse
@@ -77,9 +77,10 @@ class QuestionImportTests(TestCase):
self.assertEqual(Question.objects.get().choices.filter(is_correct=True).get().value, '۵')
@override_settings(INTERNAL_SERVICE_KEY='k')
class HealthEndpointTests(TestCase):
def test_reports_ok_and_counts_content(self):
response = self.client.get('/api/health')
response = self.client.get('/api/health', HTTP_X_INTERNAL_KEY='k')
self.assertEqual(response.status_code, 200)
body = response.json()
self.assertEqual(body['status'], 'ok')
@@ -88,12 +89,12 @@ class HealthEndpointTests(TestCase):
self.assertIn('questions', body['checks']['content'])
def test_never_leaks_a_secret(self):
raw = self.client.get('/api/health').content.decode()
raw = self.client.get('/api/health', HTTP_X_INTERNAL_KEY='k').content.decode()
for needle in ('sk-', 'SECRET', 'API_KEY', 'password'):
self.assertNotIn(needle, raw)
def test_needs_no_authentication(self):
self.assertEqual(self.client.get('/api/health').status_code, 200)
self.assertEqual(self.client.get('/api/health', HTTP_X_INTERNAL_KEY='k').status_code, 200)
class CurriculumImportTests(TestCase):
@@ -169,19 +170,21 @@ class CurriculumImportTests(TestCase):
self.assertEqual(Lesson.objects.get().title, 'ضرب توان‌ها')
@override_settings(INTERNAL_SERVICE_KEY='k')
class HealthWarningTests(TestCase):
def test_debug_mode_is_reported_as_a_warning(self):
with self.settings(DEBUG=True, DEV_MASTER_OTP='0000'):
body = self.client.get('/api/health').json()
body = self.client.get('/api/health', HTTP_X_INTERNAL_KEY='k').json()
joined = ' '.join(body['warnings'])
self.assertIn('DEBUG', joined)
self.assertIn('0000', joined)
def test_allowed_origins_include_the_deployed_frontend(self):
body = self.client.get('/api/health').json()
body = self.client.get('/api/health', HTTP_X_INTERNAL_KEY='k').json()
self.assertIn('https://math.tadrisino2.ir', body['allowedOrigins'])
@override_settings(INTERNAL_SERVICE_KEY='k')
class MigrationCheckTests(TestCase):
"""
این بررسی مهم‌ترین چیزی‌ست که /api/health می‌گوید، و تنها وقتی اهمیت دارد
@@ -231,6 +234,7 @@ class ReplaceCountTests(TestCase):
self.assertEqual(result['removed'], 1, 'باید ۱ باشد نه ۵')
@override_settings(INTERNAL_SERVICE_KEY='k')
class HealthTrafficTests(TestCase):
"""
سیگنالِ «کسی وصل شده یا نه» — تنها راهی که از بیرون می‌شود فهمید فرانت
@@ -243,7 +247,7 @@ class HealthTrafficTests(TestCase):
from accounts.models import User
User.objects.create(phone='09120000009', last_activity_at=timezone.now())
traffic = self.client.get('/api/health').json()['traffic']
traffic = self.client.get('/api/health', HTTP_X_INTERNAL_KEY='k').json()['traffic']
self.assertEqual(traffic['users'], 1)
self.assertEqual(traffic['activeLast24h'], 1)
@@ -258,7 +262,7 @@ class HealthTrafficTests(TestCase):
User.objects.create(phone='09120000010',
last_activity_at=timezone.now() - timedelta(days=3))
traffic = self.client.get('/api/health').json()['traffic']
traffic = self.client.get('/api/health', HTTP_X_INTERNAL_KEY='k').json()['traffic']
self.assertEqual(traffic['users'], 1)
self.assertEqual(traffic['activeLast24h'], 0)
@@ -266,7 +270,7 @@ class HealthTrafficTests(TestCase):
from accounts.models import User
User.objects.create(phone='09121234567', name='علی')
raw = self.client.get('/api/health').content.decode()
raw = self.client.get('/api/health', HTTP_X_INTERNAL_KEY='k').content.decode()
self.assertNotIn('09121234567', raw)
self.assertNotIn('علی', raw)