Почему в Django check_password=True, а authenticate=None
Я пытаюсь написать модульный тест для конечной точки входа в Django, используя как можно больше встроенной функциональности.
Существуют тесты, подтверждающие, что конечная точка создания учетной записи работает правильно.
В представлении входа в систему, однако, функция check_password()
вернет True
для этого теста, но функция authenticate()
вернет None
.
Безопасно ли вместо этого использовать функцию check_password()
?
Иначе, как мне обновить этот код, чтобы использовать функцию authenticate()
?
accounts.py
class Account(AbstractUser):
username = models.CharField(max_length=150, unique=True, null=False, default='')
password = models.CharField(max_length=100)
...
REQUIRED_FIELDS = ['username', 'password']
class Meta:
app_label = 'accounts'
db_table = 'accounts_account'
objects = models.Manager()
test_login.py
def test_login(self):
# Create account
request_create = self.factory.post('/accounts/account',
self.request_data,
content_type='application/json')
view = AccountView.as_view()
response_create = view(request_create)
# Login account
request_login = self.factory.post('/accounts/login',
self.request_data,
content_type='application/json')
view = LoginView.as_view()
response = view(request_login)
views.py
class LoginView(View):
def post(self, request):
r = json.loads(request.body)
username = r.get('username')
password = r.get('password')
cp = check_password(password, Account.objects.get(username=username).password)
user = authenticate(username=username, password=password)
P.S. Я проверил этот поток и is_active
установлен в true.
Это безопасно. Основное различие заключается в том, что при использовании check_password() вы вручную проверяете пользователя из бэкенда аутентификации, поэтому вы должны получить объект пользователя и сравнить его пароль с открытым текстом, как это делается в:
check_password(password, Account.objects.get(username=username).password)
В то время как с помощью authenticate() можно проверить учетные данные на нескольких бэкендах аутентификации. Это означает, что в первом случае вы не сможете подключить ваше приложение к другим источникам аутентификации
Вам не хватает некоторого кода в tests.py
и views.py
. Тем не менее, вот полный тест этого LoginView
:
tests.py
class TestClientLogin(TestCase):
def setUp(self):
User = get_user_model()
self.factory = RequestFactory()
self.user = User.objects.create_user(
username='test_user',
email='test_user@example.com',
password='super_secret'
)
def test_user_login(self):
request_data = {'username': 'test_user', 'password': 'super_secret'}
request = self.factory.post(
'/accounts/login/',
request_data ,
content_type='application/json'
)
response = LoginView.as_view()(request)
data = json.loads(response.content)
self.assertEqual(self.user.username, data['username'])
self.assertEqual(response.status_code, 200)
views.py
class LoginView(View):
def post(self, request):
data = json.loads(request.body)
username = data.get('username')
password = data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
return JsonResponse({'username': user.username})
else:
return JsonResponse({'username': None})
Если вы хотите использовать как можно больше встроенной функциональности , :
settings.py:
AUTH_USER_MODEL = 'accounts.Account'
accounts/models.py:
class Account(AbstractUser):
# you dont need to define username or password again
...
REQUIRED_FIELDS = ['username', 'password']
class Meta:
# you don't need to define default app_label, db_table
objects = UserManager() # otherwise don't work "python manage.py createsuperuser"
accounts/views.py:
from django.contrib.auth.views import LoginView
class ClientLogin(LoginView):
pass
accounts/tests.py:
from django.test import TestCase, Client
from django.utils.crypto import get_random_string
class TestClientLogin(TestCase):
def setUp(self):
User = get_user_model()
self.username, self.password = get_random_string(20), get_random_string(20) # don't use constants in tests
self.user = User.objects.create_user(username=self.username, password=self.password )
def test_user_login(self):
request_data = {'username': self.username, 'password': self.password}
response = Client().post('/accounts/login/', request_data)
self.assertEqual(response.status_code, 302)
BUT:
вам не нужно тестировать ClientLogin
, у вас нет кода в view.