Django Rest Framework Testing

huangapple go评论60阅读模式
英文:

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(&#39;username&#39;, &#39;&#39;)
        password = attrs.get(&#39;password&#39;, &#39;&#39;)
        user = auth.authenticate(username=username, password=password)
        if user:
            if user.is_active is False:
                raise AuthenticationFailed(
                    &#39;Account is disabled, contact admin&#39;)
            if not user.is_verified:
                raise AuthenticationFailed(&#39;Email is not verified&#39;)
            return {
                &#39;username&#39;: user.username,
                &#39;firstname&#39;: user.firstname,
                &#39;lastname&#39;: user.lastname,
                &#39;role&#39;: user.role,
                &#39;tokens&#39;: user.tokens
            }
        else:
            raise AuthenticationFailed(&#39;Invalid credentials, try again&#39;)

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=&#39;json&#39;)
            user = User.objects.get(username=valid_user[&#39;username&#39;])
            user.is_verified = True
            user.is_active = False
            user.save()
            response = self.client.post(
                self.login_url, valid_login_user, format=&#39;json&#39;)
            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 = [
        &#39;django.contrib.auth.backends.AllowAllUsersModelBackend&#39;] 
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(&#39;Invalid credentials, try again&#39;)`
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>

huangapple
  • 本文由 发表于 2023年2月16日 16:03:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/75469344.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定