英文:
Django Rest Framework Testing
问题
以下是您要翻译的代码部分:
I have a LoginSerializer that has the block of code as below
def validate(self, attrs):
username = attrs.get('username', '')
password = attrs.get('password', '')
user = auth.authenticate(username=username, password=password)
if user:
if user.is_active is False:
raise AuthenticationFailed(
'Account is disabled, contact admin')
if not user.is_verified:
raise AuthenticationFailed('Email is not verified')
return {
'username': user.username,
'firstname': user.firstname,
'lastname': user.lastname,
'role': user.role,
'tokens': user.tokens
}
else:
raise AuthenticationFailed('Invalid credentials, try again')
and a test case as below;
class UserLoginTest(BaseTest):
def test_inactive_user_can_login(self):
self.client.post(
self.register_public, data=valid_user, format='json')
user = User.objects.get(username=valid_user['username'])
user.is_verified = True
user.is_active = False
user.save()
response = self.client.post(
self.login_url, valid_login_user, format='json')
print(response.data)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
When I run the test with **is_active = False** I get Invalid credentials, try again. Why is it that when is_active=False the user is not found even though the user is there? Same with when I try to login from swagger.
**EDIT**
I have read that I can use
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.AllowAllUsersModelBackend']
then I will be able to check for is_active manually otherwise django handles that and returns a None. What are the dangers of doing this?
<details>
<summary>英文:</summary>
I have a LoginSerializer that has the block of code as below
def validate(self, attrs):
username = attrs.get('username', '')
password = attrs.get('password', '')
user = auth.authenticate(username=username, password=password)
if user:
if user.is_active is False:
raise AuthenticationFailed(
'Account is disabled, contact admin')
if not user.is_verified:
raise AuthenticationFailed('Email is not verified')
return {
'username': user.username,
'firstname': user.firstname,
'lastname': user.lastname,
'role': user.role,
'tokens': user.tokens
}
else:
raise AuthenticationFailed('Invalid credentials, try again')
and a test case as below;
class UserLoginTest(BaseTest):
def test_inactive_user_can_login(self):
self.client.post(
self.register_public, data=valid_user, format='json')
user = User.objects.get(username=valid_user['username'])
user.is_verified = True
user.is_active = False
user.save()
response = self.client.post(
self.login_url, valid_login_user, format='json')
print(response.data)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
When I run the test with **is_active = False** I get Invalid credentials, try again. Why is it that when is_active=False the user is not found even though the user is there? Same with when I try to login from swagger.
**EDIT**
I have read that I can use
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.AllowAllUsersModelBackend']
then I will be able to check for is_active manually otherwise django handles that and returns a **None**. What are the dangers of doing this?
</details>
# 答案1
**得分**: 1
这是因为您正在使用[`.authenticate()`][1],默认情况下会通过[`AUTHENTICATION_BACKENDS`][2]中列出的所有后端进行验证。如果在设置中没有列出,将使用`django.contrib.auth.backends.ModelBackend`,该后端验证`.is_active()`字段:
...
def user_can_authenticate(self, user):
"""
拒绝is_active=False的用户。允许没有该属性的自定义用户模型。
"""
is_active = getattr(user, "is_active", None)
return is_active or is_active is None
...
此片段在您的验证之前运行,并将`None`返回给`user`变量。因此,在`if user`条件(用户为None)失败,从而引发`AuthenticationFailed('Invalid credentials, try again')`错误。
关于[`AllowAllUsersModelBackend`][3],它唯一的作用是覆盖此方法以允许非活动用户登录:
class AllowAllUsersModelBackend(ModelBackend):
def user_can_authenticate(self, user):
return True
唯一的风险我能看到的是使用此后端而不手动检查`.is_active()`字段。除非打算允许非活动用户登录到您的系统。
[1]: https://docs.djangoproject.com/en/4.1/topics/auth/default/#authenticating-users
[2]: https://docs.djangoproject.com/en/4.1/ref/contrib/auth/#module-django.contrib.auth.backends
[3]: https://docs.djangoproject.com/en/4.1/ref/contrib/auth/#django.contrib.auth.backends.AllowAllUsersModelBackend
<details>
<summary>英文:</summary>
This happens because you are using [`.authenticate()`][1] which by default goes through all backends listed in [`AUTHENTICATION_BACKENDS`][2]. If not listed in settings, `django.contrib.auth.backends.ModelBackend` is used, this backend verifies `.is_active()` field:
...
def user_can_authenticate(self, user):
"""
Reject users with is_active=False. Custom user models that don't have
that attribute are allowed.
"""
is_active = getattr(user, "is_active", None)
return is_active or is_active is None
...
That snippet runs before your verification and returns `None` to `user` variable. So, it fails on `if user` condition (user is None) thus `raise AuthenticationFailed('Invalid credentials, try again')`
About [`AllowAllUsersModelBackend`][3] the only thing it does is override this method to allow inactive users to login:
class AllowAllUsersModelBackend(ModelBackend):
def user_can_authenticate(self, user):
return True
The only risk i can see, is using this backend and not checking `.is_active()` field manually. Unless if it is intended that inactive users can login into your system.
[1]: https://docs.djangoproject.com/en/4.1/topics/auth/default/#authenticating-users
[2]: https://docs.djangoproject.com/en/4.1/ref/contrib/auth/#module-django.contrib.auth.backends
[3]: https://docs.djangoproject.com/en/4.1/ref/contrib/auth/#django.contrib.auth.backends.AllowAllUsersModelBackend
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论