Add admin login API and remove hardcoded secrets
- Add token-based admin login/logout/me endpoints - Bootstrap admin from ADMIN_USERNAME/ADMIN_PASSWORD in Docker entrypoint - Read ALLOWED_HOSTS and CORS from env only (no hardcoded server IPs) - Keep docs/Postman/tests free of real credentials - Cover login and auth flows with 31 local API tests Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
113
api/tests.py
113
api/tests.py
@@ -64,7 +64,10 @@ class ContactUsAPITests(APITestCase):
|
||||
{'name': 'Updated'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||
self.assertIn(response.status_code, (
|
||||
status.HTTP_401_UNAUTHORIZED,
|
||||
status.HTTP_403_FORBIDDEN,
|
||||
))
|
||||
|
||||
|
||||
class CompositionAPITests(APITestCase):
|
||||
@@ -200,7 +203,10 @@ class CompositionAPITests(APITestCase):
|
||||
{'name': 'Updated'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||
self.assertIn(response.status_code, (
|
||||
status.HTTP_401_UNAUTHORIZED,
|
||||
status.HTTP_403_FORBIDDEN,
|
||||
))
|
||||
|
||||
|
||||
class CampaignAPITests(APITestCase):
|
||||
@@ -277,3 +283,106 @@ class CampaignAPITests(APITestCase):
|
||||
}
|
||||
response = self.client.post('/api/campaigns/', payload, format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class AdminLoginAPITests(APITestCase):
|
||||
def setUp(self):
|
||||
User = get_user_model()
|
||||
self.admin_username = 'admin_test'
|
||||
self.admin_password = 'test_admin_pass_123'
|
||||
self.admin = User.objects.create_user(
|
||||
username=self.admin_username,
|
||||
password=self.admin_password,
|
||||
is_staff=True,
|
||||
is_superuser=True,
|
||||
)
|
||||
self.regular = User.objects.create_user(
|
||||
username='normaluser',
|
||||
password='normalpass123',
|
||||
is_staff=False,
|
||||
)
|
||||
|
||||
def test_admin_login_success(self):
|
||||
response = self.client.post(
|
||||
'/api/admin/login/',
|
||||
{'username': self.admin_username, 'password': self.admin_password},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertIn('token', response.data)
|
||||
self.assertEqual(response.data['user']['username'], self.admin_username)
|
||||
self.assertTrue(response.data['user']['is_staff'])
|
||||
|
||||
def test_admin_login_wrong_password(self):
|
||||
response = self.client.post(
|
||||
'/api/admin/login/',
|
||||
{'username': self.admin_username, 'password': 'wrong'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_non_staff_cannot_login(self):
|
||||
response = self.client.post(
|
||||
'/api/admin/login/',
|
||||
{'username': 'normaluser', 'password': 'normalpass123'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_admin_me_with_token(self):
|
||||
login = self.client.post(
|
||||
'/api/admin/login/',
|
||||
{'username': self.admin_username, 'password': self.admin_password},
|
||||
format='json',
|
||||
)
|
||||
token = login.data['token']
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f'Token {token}')
|
||||
response = self.client.get('/api/admin/me/')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['username'], self.admin_username)
|
||||
|
||||
def test_admin_me_requires_auth(self):
|
||||
response = self.client.get('/api/admin/me/')
|
||||
self.assertIn(response.status_code, (
|
||||
status.HTTP_401_UNAUTHORIZED,
|
||||
status.HTTP_403_FORBIDDEN,
|
||||
))
|
||||
|
||||
def test_token_allows_protected_update(self):
|
||||
contact = ContactUs.objects.create(
|
||||
name='Old Name',
|
||||
email_or_phone='a@b.com',
|
||||
description='msg',
|
||||
category='سایر',
|
||||
)
|
||||
login = self.client.post(
|
||||
'/api/admin/login/',
|
||||
{'username': self.admin_username, 'password': self.admin_password},
|
||||
format='json',
|
||||
)
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f'Token {login.data["token"]}')
|
||||
response = self.client.patch(
|
||||
f'/api/contact-us/{contact.id}/',
|
||||
{'name': 'New Name'},
|
||||
format='json',
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data['name'], 'New Name')
|
||||
|
||||
def test_admin_logout(self):
|
||||
login = self.client.post(
|
||||
'/api/admin/login/',
|
||||
{'username': self.admin_username, 'password': self.admin_password},
|
||||
format='json',
|
||||
)
|
||||
token = login.data['token']
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f'Token {token}')
|
||||
response = self.client.post('/api/admin/logout/')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
# Token should no longer work
|
||||
response = self.client.get('/api/admin/me/')
|
||||
self.assertIn(response.status_code, (
|
||||
status.HTTP_401_UNAUTHORIZED,
|
||||
status.HTTP_403_FORBIDDEN,
|
||||
))
|
||||
|
||||
Reference in New Issue
Block a user