使用安全删除的用户进行DRF和令牌身份验证?

时间:2019-05-21 11:48:48

标签: python django django-rest-framework soft-delete

我正在使用一个名为django-safedelete的Django程序包,该程序包允许删除用户而不将其从数据库中删除。

基本上,它会向模型添加delete属性,而类似User.objects.all()之类的查询将不会返回已删除的模型。

您仍然可以使用特殊管理器查询所有对象。例如User.objects.all_with_deleted()将返回所有用户,包括已删除的用户。 User.objects.deleted_only()将返回已删除的内容。

这在预期的情况下起作用,除了一种情况。 我正在使用带有Django Rest Framework 3.9的用户使用令牌身份验证,并且在我的DRF视图中,我正在使用内置权限IsAuthenticated

我正在使用的基本CBV代码:

class MyView(APIView):

    permission_classes = (IsAuthenticated,)

    def get(self, request):
        return Response(status=HTTP_200_OK)

IsAuthenticated权限的DRF实现的代码:

class IsAuthenticated(BasePermission):
    """
    Allows access only to authenticated users.
    """

    def has_permission(self, request, view):
        return bool(request.user and request.user.is_authenticated)

问题

用户被软删除后,他仍然可以使用其令牌进行身份验证。

我希望用户被软删除时出现401未经授权的错误。 怎么了?

2 个答案:

答案 0 :(得分:6)

为什么?

如果我们查看DRF TokenAuthentication [source-code] authenticate_credentials() 方法,我们会看到,

def authenticate_credentials(self, key):
    model = self.get_model()
    try:
        token = model.objects.select_related('user').get(key=key)
    except model.DoesNotExist:
        raise exceptions.AuthenticationFailed(_('Invalid token.'))

    if not token.user.is_active:
        raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))

    return (token.user, token)

这表示它没有过滤出 软删除的用户实例

解决方案?

创建自定义身份验证类并在相应的视图中进行连接

# authentication.py
from rest_framework.authentication import TokenAuthentication, exceptions, _


class CustomTokenAuthentication(TokenAuthentication):
    def authenticate_credentials(self, key):
        model = self.get_model()
        try:
            token = model.objects.select_related('user').get(key=key)
        except model.DoesNotExist:
            raise exceptions.AuthenticationFailed(_('Invalid token.'))

        if not token.user.is_active or not token.user.deleted: # Here I added something new !!
            raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))

        return (token.user, token)

连接视图

# views.py
from rest_framework.views import APIView


class MyView(APIView):
    authentication_classes = (CustomTokenAuthentication,)
    permission_classes = (IsAuthenticated,)

    def get(self, request):
        return Response(status=HTTP_200_OK)

答案 1 :(得分:6)

DRF已经使用is_active属性来确定用户是否可以进行身份​​验证。每当您删除用户时,只需确保同时将is_active设置为False

对于Django安全删除:

由于您使用的是django-safedelete,因此必须覆盖delete()方法才能停用,然后使用super()来执行原始行为,例如:

class MyUserModel(SafeDeleteModel):
    _safedelete_policy = SOFT_DELETE
    my_field = models.TextField()

    def delete(self, *args, **kwargs):
        self.is_active = False
        super().delete(*args, **kwargs)

    def undelete(self, *args, **kwargs):
        self.is_active = True
        super().undelete(*args, **kwargs)

请注意,这也适用于QuerySet,因为SafeDeleteModel的管理器会覆盖QuerySet delete()方法。 (请参阅:https://github.com/makinacorpus/django-safedelete/blob/master/safedelete/queryset.py

此解决方案的好处是您不必在每个APIView上都更改auth类,并且依赖于is_active模型的User属性的所有应用程序都将表现出合理的行为。另外,如果您不这样做,那么您将删除也处于活动状态的对象,因此没有太大意义。